1 /*
2  * Copyright (c) 2018 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <logging/log.h>
8 LOG_MODULE_REGISTER(log_backend_net, CONFIG_LOG_DEFAULT_LEVEL);
9 
10 #include <logging/log_backend.h>
11 #include <logging/log_core.h>
12 #include <logging/log_output.h>
13 #include <logging/log_msg.h>
14 #include <net/net_pkt.h>
15 #include <net/net_context.h>
16 
17 /* Set this to 1 if you want to see what is being sent to server */
18 #define DEBUG_PRINTING 0
19 
20 #if DEBUG_PRINTING
21 #define DBG(fmt, ...) printk(fmt, ##__VA_ARGS__)
22 #else
23 #define DBG(fmt, ...)
24 #endif
25 
26 #if defined(CONFIG_NET_IPV6) || CONFIG_NET_HOSTNAME_ENABLE
27 #define MAX_HOSTNAME_LEN NET_IPV6_ADDR_LEN
28 #else
29 #define MAX_HOSTNAME_LEN NET_IPV4_ADDR_LEN
30 #endif
31 
32 static char dev_hostname[MAX_HOSTNAME_LEN + 1];
33 
34 static uint8_t output_buf[CONFIG_LOG_BACKEND_NET_MAX_BUF_SIZE];
35 static bool net_init_done;
36 struct sockaddr server_addr;
37 static bool panic_mode;
38 
39 const struct log_backend *log_backend_net_get(void);
40 
41 NET_PKT_SLAB_DEFINE(syslog_tx_pkts, CONFIG_LOG_BACKEND_NET_MAX_BUF);
42 NET_PKT_DATA_POOL_DEFINE(syslog_tx_bufs,
43 			 ROUND_UP(CONFIG_LOG_BACKEND_NET_MAX_BUF_SIZE /
44 				  CONFIG_NET_BUF_DATA_SIZE, 1) *
45 			 CONFIG_LOG_BACKEND_NET_MAX_BUF);
46 
get_tx_slab(void)47 static struct k_mem_slab *get_tx_slab(void)
48 {
49 	return &syslog_tx_pkts;
50 }
51 
get_data_pool(void)52 struct net_buf_pool *get_data_pool(void)
53 {
54 	return &syslog_tx_bufs;
55 }
56 
line_out(uint8_t * data,size_t length,void * output_ctx)57 static int line_out(uint8_t *data, size_t length, void *output_ctx)
58 {
59 	struct net_context *ctx = (struct net_context *)output_ctx;
60 	int ret = -ENOMEM;
61 
62 	if (ctx == NULL) {
63 		return length;
64 	}
65 
66 	ret = net_context_send(ctx, data, length, NULL, K_NO_WAIT, NULL);
67 	if (ret < 0) {
68 		goto fail;
69 	}
70 
71 	DBG(data);
72 fail:
73 	return length;
74 }
75 
76 LOG_OUTPUT_DEFINE(log_output_net, line_out, output_buf, sizeof(output_buf));
77 
do_net_init(void)78 static int do_net_init(void)
79 {
80 	struct sockaddr *local_addr = NULL;
81 	struct sockaddr_in6 local_addr6;
82 	struct sockaddr_in local_addr4;
83 	socklen_t server_addr_len;
84 	struct net_context *ctx;
85 	int ret;
86 
87 	if (IS_ENABLED(CONFIG_NET_IPV4) && server_addr.sa_family == AF_INET) {
88 		local_addr = (struct sockaddr *)&local_addr4;
89 		server_addr_len = sizeof(struct sockaddr_in);
90 		local_addr4.sin_port = 0U;
91 	}
92 
93 	if (IS_ENABLED(CONFIG_NET_IPV6) && server_addr.sa_family == AF_INET6) {
94 		local_addr = (struct sockaddr *)&local_addr6;
95 		server_addr_len = sizeof(struct sockaddr_in6);
96 		local_addr6.sin6_port = 0U;
97 	}
98 
99 	if (local_addr == NULL) {
100 		DBG("Server address unknown\n");
101 		return -EINVAL;
102 	}
103 
104 	local_addr->sa_family = server_addr.sa_family;
105 
106 	ret = net_context_get(server_addr.sa_family, SOCK_DGRAM, IPPROTO_UDP,
107 			      &ctx);
108 	if (ret < 0) {
109 		DBG("Cannot get context (%d)\n", ret);
110 		return ret;
111 	}
112 
113 	if (IS_ENABLED(CONFIG_NET_HOSTNAME_ENABLE)) {
114 		(void)strncpy(dev_hostname, net_hostname_get(), MAX_HOSTNAME_LEN);
115 
116 	} else if (IS_ENABLED(CONFIG_NET_IPV6) &&
117 		   server_addr.sa_family == AF_INET6) {
118 		const struct in6_addr *src;
119 
120 		src = net_if_ipv6_select_src_addr(
121 			NULL, &net_sin6(&server_addr)->sin6_addr);
122 		if (src) {
123 			net_addr_ntop(AF_INET6, src, dev_hostname,
124 				      MAX_HOSTNAME_LEN);
125 
126 			net_ipaddr_copy(&local_addr6.sin6_addr, src);
127 		} else {
128 			goto unknown;
129 		}
130 
131 	} else if (IS_ENABLED(CONFIG_NET_IPV4) &&
132 		   server_addr.sa_family == AF_INET) {
133 		const struct in_addr *src;
134 
135 		src = net_if_ipv4_select_src_addr(
136 				  NULL, &net_sin(&server_addr)->sin_addr);
137 
138 		if (src) {
139 			net_addr_ntop(AF_INET, src, dev_hostname,
140 				      MAX_HOSTNAME_LEN);
141 
142 			net_ipaddr_copy(&local_addr4.sin_addr, src);
143 		} else {
144 			goto unknown;
145 		}
146 
147 	} else {
148 	unknown:
149 		DBG("Cannot setup local context\n");
150 		return -EINVAL;
151 	}
152 
153 	ret = net_context_bind(ctx, local_addr, server_addr_len);
154 	if (ret < 0) {
155 		DBG("Cannot bind context (%d)\n", ret);
156 		return ret;
157 	}
158 
159 	(void)net_context_connect(ctx, &server_addr, server_addr_len,
160 				  NULL, K_NO_WAIT, NULL);
161 
162 	/* We do not care about return value for this UDP connect call that
163 	 * basically does nothing. Calling the connect is only useful so that
164 	 * we can see the syslog connection in net-shell.
165 	 */
166 
167 	net_context_setup_pools(ctx, get_tx_slab, get_data_pool);
168 
169 	log_output_ctx_set(&log_output_net, ctx);
170 	log_output_hostname_set(&log_output_net, dev_hostname);
171 
172 	return 0;
173 }
174 
send_output(const struct log_backend * const backend,struct log_msg * msg)175 static void send_output(const struct log_backend *const backend,
176 			struct log_msg *msg)
177 {
178 	if (panic_mode) {
179 		return;
180 	}
181 
182 	if (!net_init_done && do_net_init() == 0) {
183 		net_init_done = true;
184 	}
185 
186 	log_msg_get(msg);
187 
188 	log_output_msg_process(&log_output_net, msg,
189 			       LOG_OUTPUT_FLAG_FORMAT_SYSLOG |
190 			       LOG_OUTPUT_FLAG_TIMESTAMP |
191 			(IS_ENABLED(CONFIG_LOG_BACKEND_NET_SYST_ENABLE) ?
192 			LOG_OUTPUT_FLAG_FORMAT_SYST : 0));
193 
194 	log_msg_put(msg);
195 }
196 
process(const struct log_backend * const backend,union log_msg2_generic * msg)197 static void process(const struct log_backend *const backend,
198 		    union log_msg2_generic *msg)
199 {
200 	uint32_t flags = LOG_OUTPUT_FLAG_FORMAT_SYSLOG | LOG_OUTPUT_FLAG_TIMESTAMP;
201 
202 	if (panic_mode) {
203 		return;
204 	}
205 
206 	if (!net_init_done && do_net_init() == 0) {
207 		net_init_done = true;
208 	}
209 
210 	log_output_msg2_process(&log_output_net, &msg->log, flags);
211 }
212 
init_net(struct log_backend const * const backend)213 static void init_net(struct log_backend const *const backend)
214 {
215 	ARG_UNUSED(backend);
216 	int ret;
217 
218 	net_sin(&server_addr)->sin_port = htons(514);
219 
220 	ret = net_ipaddr_parse(CONFIG_LOG_BACKEND_NET_SERVER,
221 			       sizeof(CONFIG_LOG_BACKEND_NET_SERVER) - 1,
222 			       &server_addr);
223 	if (ret == 0) {
224 		LOG_ERR("Cannot configure syslog server address");
225 		return;
226 	}
227 
228 	log_backend_deactivate(log_backend_net_get());
229 }
230 
panic(struct log_backend const * const backend)231 static void panic(struct log_backend const *const backend)
232 {
233 	panic_mode = true;
234 }
235 
sync_string(const struct log_backend * const backend,struct log_msg_ids src_level,uint32_t timestamp,const char * fmt,va_list ap)236 static void sync_string(const struct log_backend *const backend,
237 		     struct log_msg_ids src_level, uint32_t timestamp,
238 		     const char *fmt, va_list ap)
239 {
240 	uint32_t flags = LOG_OUTPUT_FLAG_LEVEL | LOG_OUTPUT_FLAG_FORMAT_SYSLOG |
241 		LOG_OUTPUT_FLAG_TIMESTAMP |
242 		(IS_ENABLED(CONFIG_LOG_BACKEND_NET_SYST_ENABLE) ?
243 		LOG_OUTPUT_FLAG_FORMAT_SYST : 0);
244 	uint32_t key;
245 
246 	if (!net_init_done && do_net_init() == 0) {
247 		net_init_done = true;
248 	}
249 
250 	key = irq_lock();
251 	log_output_string(&log_output_net, src_level,
252 			  timestamp, fmt, ap, flags);
253 	irq_unlock(key);
254 }
255 
256 const struct log_backend_api log_backend_net_api = {
257 	.panic = panic,
258 	.init = init_net,
259 	.process = IS_ENABLED(CONFIG_LOG2) ? process : NULL,
260 	.put = IS_ENABLED(CONFIG_LOG_MODE_DEFERRED) ? send_output : NULL,
261 	.put_sync_string = IS_ENABLED(CONFIG_LOG_MODE_IMMEDIATE) ?
262 							sync_string : NULL,
263 	/* Currently we do not send hexdumps over network to remote server
264 	 * in CONFIG_LOG_IMMEDIATE mode. This is just to save resources,
265 	 * this can be revisited if needed.
266 	 */
267 	.put_sync_hexdump = NULL,
268 };
269 
270 /* Note that the backend can be activated only after we have networking
271  * subsystem ready so we must not start it immediately.
272  */
273 LOG_BACKEND_DEFINE(log_backend_net, log_backend_net_api,
274 		   IS_ENABLED(CONFIG_LOG_BACKEND_NET_AUTOSTART));
275 
log_backend_net_get(void)276 const struct log_backend *log_backend_net_get(void)
277 {
278 	return &log_backend_net;
279 }
280