1 /*
2  * Copyright (c) 2018 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/logging/log.h>
8 LOG_MODULE_REGISTER(log_backend_net, CONFIG_LOG_DEFAULT_LEVEL);
9 
10 #include <zephyr/logging/log_backend.h>
11 #include <zephyr/logging/log_core.h>
12 #include <zephyr/logging/log_output.h>
13 #include <zephyr/logging/log_backend_net.h>
14 #include <zephyr/net/net_pkt.h>
15 #include <zephyr/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 static uint32_t log_format_current = CONFIG_LOG_BACKEND_NET_OUTPUT_DEFAULT;
39 
40 const struct log_backend *log_backend_net_get(void);
41 
42 NET_PKT_SLAB_DEFINE(syslog_tx_pkts, CONFIG_LOG_BACKEND_NET_MAX_BUF);
43 NET_PKT_DATA_POOL_DEFINE(syslog_tx_bufs,
44 			 ROUND_UP(CONFIG_LOG_BACKEND_NET_MAX_BUF_SIZE /
45 				  CONFIG_NET_BUF_DATA_SIZE, 1) *
46 			 CONFIG_LOG_BACKEND_NET_MAX_BUF);
47 
get_tx_slab(void)48 static struct k_mem_slab *get_tx_slab(void)
49 {
50 	return &syslog_tx_pkts;
51 }
52 
get_data_pool(void)53 struct net_buf_pool *get_data_pool(void)
54 {
55 	return &syslog_tx_bufs;
56 }
57 
line_out(uint8_t * data,size_t length,void * output_ctx)58 static int line_out(uint8_t *data, size_t length, void *output_ctx)
59 {
60 	struct net_context *ctx = (struct net_context *)output_ctx;
61 	int ret = -ENOMEM;
62 
63 	if (ctx == NULL) {
64 		return length;
65 	}
66 
67 	ret = net_context_send(ctx, data, length, NULL, K_NO_WAIT, NULL);
68 	if (ret < 0) {
69 		goto fail;
70 	}
71 
72 	DBG(data);
73 fail:
74 	return length;
75 }
76 
77 LOG_OUTPUT_DEFINE(log_output_net, line_out, output_buf, sizeof(output_buf));
78 
do_net_init(void)79 static int do_net_init(void)
80 {
81 	struct sockaddr *local_addr = NULL;
82 	struct sockaddr_in6 local_addr6 = {0};
83 	struct sockaddr_in local_addr4 = {0};
84 	socklen_t server_addr_len;
85 	struct net_context *ctx;
86 	int ret;
87 
88 	if (IS_ENABLED(CONFIG_NET_IPV4) && server_addr.sa_family == AF_INET) {
89 		local_addr = (struct sockaddr *)&local_addr4;
90 		server_addr_len = sizeof(struct sockaddr_in);
91 		local_addr4.sin_port = 0U;
92 	}
93 
94 	if (IS_ENABLED(CONFIG_NET_IPV6) && server_addr.sa_family == AF_INET6) {
95 		local_addr = (struct sockaddr *)&local_addr6;
96 		server_addr_len = sizeof(struct sockaddr_in6);
97 		local_addr6.sin6_port = 0U;
98 	}
99 
100 	if (local_addr == NULL) {
101 		DBG("Server address unknown\n");
102 		return -EINVAL;
103 	}
104 
105 	local_addr->sa_family = server_addr.sa_family;
106 
107 	ret = net_context_get(server_addr.sa_family, SOCK_DGRAM, IPPROTO_UDP,
108 			      &ctx);
109 	if (ret < 0) {
110 		DBG("Cannot get context (%d)\n", ret);
111 		return ret;
112 	}
113 
114 	if (IS_ENABLED(CONFIG_NET_HOSTNAME_ENABLE)) {
115 		(void)strncpy(dev_hostname, net_hostname_get(), MAX_HOSTNAME_LEN);
116 
117 	} else if (IS_ENABLED(CONFIG_NET_IPV6) &&
118 		   server_addr.sa_family == AF_INET6) {
119 		const struct in6_addr *src;
120 
121 		src = net_if_ipv6_select_src_addr(
122 			NULL, &net_sin6(&server_addr)->sin6_addr);
123 		if (src) {
124 			net_addr_ntop(AF_INET6, src, dev_hostname,
125 				      MAX_HOSTNAME_LEN);
126 
127 			net_ipaddr_copy(&local_addr6.sin6_addr, src);
128 		} else {
129 			goto unknown;
130 		}
131 
132 	} else if (IS_ENABLED(CONFIG_NET_IPV4) &&
133 		   server_addr.sa_family == AF_INET) {
134 		const struct in_addr *src;
135 
136 		src = net_if_ipv4_select_src_addr(
137 				  NULL, &net_sin(&server_addr)->sin_addr);
138 
139 		if (src) {
140 			net_addr_ntop(AF_INET, src, dev_hostname,
141 				      MAX_HOSTNAME_LEN);
142 
143 			net_ipaddr_copy(&local_addr4.sin_addr, src);
144 		} else {
145 			goto unknown;
146 		}
147 
148 	} else {
149 	unknown:
150 		DBG("Cannot setup local context\n");
151 		return -EINVAL;
152 	}
153 
154 	ret = net_context_bind(ctx, local_addr, server_addr_len);
155 	if (ret < 0) {
156 		DBG("Cannot bind context (%d)\n", ret);
157 		return ret;
158 	}
159 
160 	(void)net_context_connect(ctx, &server_addr, server_addr_len,
161 				  NULL, K_NO_WAIT, NULL);
162 
163 	/* We do not care about return value for this UDP connect call that
164 	 * basically does nothing. Calling the connect is only useful so that
165 	 * we can see the syslog connection in net-shell.
166 	 */
167 
168 	net_context_setup_pools(ctx, get_tx_slab, get_data_pool);
169 
170 	log_output_ctx_set(&log_output_net, ctx);
171 	log_output_hostname_set(&log_output_net, dev_hostname);
172 
173 	return 0;
174 }
175 
process(const struct log_backend * const backend,union log_msg_generic * msg)176 static void process(const struct log_backend *const backend,
177 		    union log_msg_generic *msg)
178 {
179 	uint32_t flags = LOG_OUTPUT_FLAG_FORMAT_SYSLOG | LOG_OUTPUT_FLAG_TIMESTAMP;
180 
181 	if (panic_mode) {
182 		return;
183 	}
184 
185 	if (!net_init_done && do_net_init() == 0) {
186 		net_init_done = true;
187 	}
188 
189 	log_format_func_t log_output_func = log_format_func_t_get(log_format_current);
190 
191 	log_output_func(&log_output_net, &msg->log, flags);
192 }
193 
format_set(const struct log_backend * const backend,uint32_t log_type)194 static int format_set(const struct log_backend *const backend, uint32_t log_type)
195 {
196 	log_format_current = log_type;
197 	return 0;
198 }
199 
log_backend_net_set_addr(const char * addr)200 bool log_backend_net_set_addr(const char *addr)
201 {
202 	bool ret = false;
203 
204 	if (net_init_done) {
205 		/* Release context so it can be recreated with the specified ip address
206 		 * next time process() is called
207 		 */
208 		int released = net_context_put(log_output_net.control_block->ctx);
209 
210 		if (released < 0) {
211 			LOG_ERR("Cannot release context (%d)", ret);
212 			ret = false;
213 		} else {
214 			/* The context is successfully released so we flag it
215 			 * to be recreated with the new ip address
216 			 */
217 			net_init_done = false;
218 			ret = true;
219 		}
220 
221 		if (!ret) {
222 			return ret;
223 		}
224 	}
225 
226 	net_sin(&server_addr)->sin_port = htons(514);
227 
228 	ret = net_ipaddr_parse(addr, strlen(addr), &server_addr);
229 
230 	if (!ret) {
231 		LOG_ERR("Cannot parse syslog server address");
232 		return ret;
233 	}
234 
235 	return ret;
236 }
237 
init_net(struct log_backend const * const backend)238 static void init_net(struct log_backend const *const backend)
239 {
240 	ARG_UNUSED(backend);
241 
242 	if (strlen(CONFIG_LOG_BACKEND_NET_SERVER) != 0) {
243 		bool ret = log_backend_net_set_addr(CONFIG_LOG_BACKEND_NET_SERVER);
244 
245 		if (!ret) {
246 			return;
247 		}
248 	}
249 
250 	log_backend_deactivate(log_backend_net_get());
251 }
252 
panic(struct log_backend const * const backend)253 static void panic(struct log_backend const *const backend)
254 {
255 	panic_mode = true;
256 }
257 
258 const struct log_backend_api log_backend_net_api = {
259 	.panic = panic,
260 	.init = init_net,
261 	.process = process,
262 	.format_set = format_set,
263 };
264 
265 /* Note that the backend can be activated only after we have networking
266  * subsystem ready so we must not start it immediately.
267  */
268 LOG_BACKEND_DEFINE(log_backend_net, log_backend_net_api,
269 		   IS_ENABLED(CONFIG_LOG_BACKEND_NET_AUTOSTART));
270 
log_backend_net_get(void)271 const struct log_backend *log_backend_net_get(void)
272 {
273 	return &log_backend_net;
274 }
275