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