1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include <net/if.h>
4 #include <netinet/in.h>
5 #include <linux/if_arp.h>
6 
7 #include "conf-parser.h"
8 #include "alloc-util.h"
9 #include "extract-word.h"
10 #include "string-table.h"
11 #include "string-util.h"
12 #include "strv.h"
13 #include "parse-util.h"
14 #include "vxlan.h"
15 
16 static const char* const df_table[_NETDEV_VXLAN_DF_MAX] = {
17         [NETDEV_VXLAN_DF_NO] = "no",
18         [NETDEV_VXLAN_DF_YES] = "yes",
19         [NETDEV_VXLAN_DF_INHERIT] = "inherit",
20 };
21 
22 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(df, VxLanDF, NETDEV_VXLAN_DF_YES);
23 DEFINE_CONFIG_PARSE_ENUM(config_parse_df, df, VxLanDF, "Failed to parse VXLAN IPDoNotFragment= setting");
24 
vxlan_get_local_address(VxLan * v,Link * link,int * ret_family,union in_addr_union * ret_address)25 static int vxlan_get_local_address(VxLan *v, Link *link, int *ret_family, union in_addr_union *ret_address) {
26         assert(v);
27 
28         if (v->local_type < 0) {
29                 if (ret_family)
30                         *ret_family = v->local_family;
31                 if (ret_address)
32                         *ret_address = v->local;
33                 return 0;
34         }
35 
36         return link_get_local_address(link, v->local_type, v->local_family, ret_family, ret_address);
37 }
38 
netdev_vxlan_fill_message_create(NetDev * netdev,Link * link,sd_netlink_message * m)39 static int netdev_vxlan_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) {
40         union in_addr_union local;
41         int local_family, r;
42         VxLan *v;
43 
44         assert(netdev);
45         assert(m);
46 
47         v = VXLAN(netdev);
48 
49         assert(v);
50 
51         if (v->vni <= VXLAN_VID_MAX) {
52                 r = sd_netlink_message_append_u32(m, IFLA_VXLAN_ID, v->vni);
53                 if (r < 0)
54                         return r;
55         }
56 
57         if (in_addr_is_set(v->group_family, &v->group)) {
58                 if (v->group_family == AF_INET)
59                         r = sd_netlink_message_append_in_addr(m, IFLA_VXLAN_GROUP, &v->group.in);
60                 else
61                         r = sd_netlink_message_append_in6_addr(m, IFLA_VXLAN_GROUP6, &v->group.in6);
62                 if (r < 0)
63                         return r;
64         } else if (in_addr_is_set(v->remote_family, &v->remote)) {
65                 if (v->remote_family == AF_INET)
66                         r = sd_netlink_message_append_in_addr(m, IFLA_VXLAN_GROUP, &v->remote.in);
67                 else
68                         r = sd_netlink_message_append_in6_addr(m, IFLA_VXLAN_GROUP6, &v->remote.in6);
69                 if (r < 0)
70                         return r;
71         }
72 
73         r = vxlan_get_local_address(v, link, &local_family, &local);
74         if (r < 0)
75                 return r;
76 
77         if (in_addr_is_set(local_family, &local)) {
78                 if (local_family == AF_INET)
79                         r = sd_netlink_message_append_in_addr(m, IFLA_VXLAN_LOCAL, &local.in);
80                 else
81                         r = sd_netlink_message_append_in6_addr(m, IFLA_VXLAN_LOCAL6, &local.in6);
82                 if (r < 0)
83                         return r;
84         }
85 
86         r = sd_netlink_message_append_u32(m, IFLA_VXLAN_LINK, link ? link->ifindex : 0);
87         if (r < 0)
88                 return r;
89 
90         if (v->inherit) {
91                 r = sd_netlink_message_append_flag(m, IFLA_VXLAN_TTL_INHERIT);
92                 if (r < 0)
93                         return r;
94         } else {
95                 r = sd_netlink_message_append_u8(m, IFLA_VXLAN_TTL, v->ttl);
96                 if (r < 0)
97                         return r;
98         }
99 
100         if (v->tos != 0) {
101                 r = sd_netlink_message_append_u8(m, IFLA_VXLAN_TOS, v->tos);
102                 if (r < 0)
103                         return r;
104         }
105 
106         r = sd_netlink_message_append_u8(m, IFLA_VXLAN_LEARNING, v->learning);
107         if (r < 0)
108                 return r;
109 
110         r = sd_netlink_message_append_u8(m, IFLA_VXLAN_RSC, v->route_short_circuit);
111         if (r < 0)
112                 return r;
113 
114         r = sd_netlink_message_append_u8(m, IFLA_VXLAN_PROXY, v->arp_proxy);
115         if (r < 0)
116                 return r;
117 
118         r = sd_netlink_message_append_u8(m, IFLA_VXLAN_L2MISS, v->l2miss);
119         if (r < 0)
120                 return r;
121 
122         r = sd_netlink_message_append_u8(m, IFLA_VXLAN_L3MISS, v->l3miss);
123         if (r < 0)
124                 return r;
125 
126         if (v->fdb_ageing != 0) {
127                 r = sd_netlink_message_append_u32(m, IFLA_VXLAN_AGEING, v->fdb_ageing / USEC_PER_SEC);
128                 if (r < 0)
129                         return r;
130         }
131 
132         if (v->max_fdb != 0) {
133                 r = sd_netlink_message_append_u32(m, IFLA_VXLAN_LIMIT, v->max_fdb);
134                 if (r < 0)
135                         return r;
136         }
137 
138         r = sd_netlink_message_append_u8(m, IFLA_VXLAN_UDP_CSUM, v->udpcsum);
139         if (r < 0)
140                 return r;
141 
142         r = sd_netlink_message_append_u8(m, IFLA_VXLAN_UDP_ZERO_CSUM6_TX, v->udp6zerocsumtx);
143         if (r < 0)
144                 return r;
145 
146         r = sd_netlink_message_append_u8(m, IFLA_VXLAN_UDP_ZERO_CSUM6_RX, v->udp6zerocsumrx);
147         if (r < 0)
148                 return r;
149 
150         r = sd_netlink_message_append_u8(m, IFLA_VXLAN_REMCSUM_TX, v->remote_csum_tx);
151         if (r < 0)
152                 return r;
153 
154         r = sd_netlink_message_append_u8(m, IFLA_VXLAN_REMCSUM_RX, v->remote_csum_rx);
155         if (r < 0)
156                 return r;
157 
158         r = sd_netlink_message_append_u16(m, IFLA_VXLAN_PORT, htobe16(v->dest_port));
159         if (r < 0)
160                 return r;
161 
162         if (v->port_range.low != 0 || v->port_range.high != 0) {
163                 struct ifla_vxlan_port_range port_range;
164 
165                 port_range.low = htobe16(v->port_range.low);
166                 port_range.high = htobe16(v->port_range.high);
167 
168                 r = sd_netlink_message_append_data(m, IFLA_VXLAN_PORT_RANGE, &port_range, sizeof(port_range));
169                 if (r < 0)
170                         return r;
171         }
172 
173         r = sd_netlink_message_append_u32(m, IFLA_VXLAN_LABEL, htobe32(v->flow_label));
174         if (r < 0)
175                 return r;
176 
177         if (v->group_policy) {
178                 r = sd_netlink_message_append_flag(m, IFLA_VXLAN_GBP);
179                 if (r < 0)
180                         return r;
181         }
182 
183         if (v->generic_protocol_extension) {
184                 r = sd_netlink_message_append_flag(m, IFLA_VXLAN_GPE);
185                 if (r < 0)
186                         return r;
187         }
188 
189         if (v->df != _NETDEV_VXLAN_DF_INVALID) {
190                 r = sd_netlink_message_append_u8(m, IFLA_VXLAN_DF, v->df);
191                 if (r < 0)
192                         return r;
193         }
194 
195         return 0;
196 }
197 
config_parse_vxlan_address(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)198 int config_parse_vxlan_address(
199                 const char *unit,
200                 const char *filename,
201                 unsigned line,
202                 const char *section,
203                 unsigned section_line,
204                 const char *lvalue,
205                 int ltype,
206                 const char *rvalue,
207                 void *data,
208                 void *userdata) {
209 
210         VxLan *v = userdata;
211         union in_addr_union *addr = data, buffer;
212         int *family, f, r;
213 
214         assert(filename);
215         assert(lvalue);
216         assert(rvalue);
217         assert(data);
218         assert(userdata);
219 
220         if (streq(lvalue, "Local"))
221                 family = &v->local_family;
222         else if (streq(lvalue, "Remote"))
223                 family = &v->remote_family;
224         else if (streq(lvalue, "Group"))
225                 family = &v->group_family;
226         else
227                 assert_not_reached();
228 
229         if (isempty(rvalue)) {
230                 *addr = IN_ADDR_NULL;
231                 *family = AF_UNSPEC;
232                 return 0;
233         }
234 
235         if (streq(lvalue, "Local")) {
236                 NetDevLocalAddressType t;
237 
238                 t = netdev_local_address_type_from_string(rvalue);
239                 if (t >= 0) {
240                         v->local = IN_ADDR_NULL;
241                         v->local_family = AF_UNSPEC;
242                         v->local_type = t;
243                         return 0;
244                 }
245         }
246 
247         r = in_addr_from_string_auto(rvalue, &f, &buffer);
248         if (r < 0) {
249                 log_syntax(unit, LOG_WARNING, filename, line, r,
250                            "Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue);
251                 return 0;
252         }
253 
254         r = in_addr_is_multicast(f, &buffer);
255 
256         if (streq(lvalue, "Group")) {
257                 if (r <= 0) {
258                         log_syntax(unit, LOG_WARNING, filename, line, 0,
259                                    "%s= must be a multicast address, ignoring assignment: %s", lvalue, rvalue);
260                         return 0;
261                 }
262         } else {
263                 if (r > 0) {
264                         log_syntax(unit, LOG_WARNING, filename, line, 0,
265                                    "%s= cannot be a multicast address, ignoring assignment: %s", lvalue, rvalue);
266                         return 0;
267                 }
268         }
269 
270         if (streq(lvalue, "Local"))
271                 v->local_type = _NETDEV_LOCAL_ADDRESS_TYPE_INVALID;
272         *addr = buffer;
273         *family = f;
274 
275         return 0;
276 }
277 
config_parse_port_range(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)278 int config_parse_port_range(
279                 const char *unit,
280                 const char *filename,
281                 unsigned line,
282                 const char *section,
283                 unsigned section_line,
284                 const char *lvalue,
285                 int ltype,
286                 const char *rvalue,
287                 void *data,
288                 void *userdata) {
289 
290         VxLan *v = userdata;
291         uint16_t low, high;
292         int r;
293 
294         assert(filename);
295         assert(lvalue);
296         assert(rvalue);
297         assert(data);
298 
299         r = parse_ip_port_range(rvalue, &low, &high);
300         if (r < 0) {
301                 log_syntax(unit, LOG_WARNING, filename, line, r,
302                            "Failed to parse VXLAN port range '%s'. Port should be greater than 0 and less than 65535.", rvalue);
303                 return 0;
304         }
305 
306         v->port_range.low = low;
307         v->port_range.high = high;
308 
309         return 0;
310 }
311 
config_parse_flow_label(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)312 int config_parse_flow_label(
313                 const char *unit,
314                 const char *filename,
315                 unsigned line,
316                 const char *section,
317                 unsigned section_line,
318                 const char *lvalue,
319                 int ltype,
320                 const char *rvalue,
321                 void *data,
322                 void *userdata) {
323 
324         VxLan *v = userdata;
325         unsigned f;
326         int r;
327 
328         assert(filename);
329         assert(lvalue);
330         assert(rvalue);
331         assert(data);
332 
333         r = safe_atou(rvalue, &f);
334         if (r < 0) {
335                 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse VXLAN flow label '%s'.", rvalue);
336                 return 0;
337         }
338 
339         if (f & ~VXLAN_FLOW_LABEL_MAX_MASK) {
340                 log_syntax(unit, LOG_WARNING, filename, line, r,
341                            "VXLAN flow label '%s' not valid. Flow label range should be [0-1048575].", rvalue);
342                 return 0;
343         }
344 
345         v->flow_label = f;
346 
347         return 0;
348 }
349 
config_parse_vxlan_ttl(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)350 int config_parse_vxlan_ttl(
351                 const char *unit,
352                 const char *filename,
353                 unsigned line,
354                 const char *section,
355                 unsigned section_line,
356                 const char *lvalue,
357                 int ltype,
358                 const char *rvalue,
359                 void *data,
360                 void *userdata) {
361 
362         VxLan *v = userdata;
363         unsigned f;
364         int r;
365 
366         assert(filename);
367         assert(lvalue);
368         assert(rvalue);
369         assert(data);
370 
371         if (streq(rvalue, "inherit"))
372                 v->inherit = true;
373         else {
374                 r = safe_atou(rvalue, &f);
375                 if (r < 0) {
376                         log_syntax(unit, LOG_WARNING, filename, line, r,
377                                    "Failed to parse VXLAN TTL '%s', ignoring assignment: %m", rvalue);
378                         return 0;
379                 }
380 
381                 if (f > 255) {
382                         log_syntax(unit, LOG_WARNING, filename, line, 0,
383                                    "Invalid VXLAN TTL '%s'. TTL must be <= 255. Ignoring assignment.", rvalue);
384                         return 0;
385                 }
386 
387                 v->ttl = f;
388         }
389 
390         return 0;
391 }
392 
netdev_vxlan_verify(NetDev * netdev,const char * filename)393 static int netdev_vxlan_verify(NetDev *netdev, const char *filename) {
394         VxLan *v = VXLAN(netdev);
395 
396         assert(netdev);
397         assert(v);
398         assert(filename);
399 
400         if (v->vni > VXLAN_VID_MAX)
401                 return log_netdev_warning_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
402                                                 "%s: VXLAN without valid VNI (or VXLAN Segment ID) configured. Ignoring.",
403                                                 filename);
404 
405         if (v->ttl > 255)
406                 return log_netdev_warning_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
407                                                 "%s: VXLAN TTL must be <= 255. Ignoring.",
408                                                 filename);
409 
410         if (!v->dest_port && v->generic_protocol_extension)
411                 v->dest_port = 4790;
412 
413         if (in_addr_is_set(v->group_family, &v->group) && in_addr_is_set(v->remote_family, &v->remote))
414                 return log_netdev_warning_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
415                                                 "%s: VXLAN both 'Group=' and 'Remote=' cannot be specified. Ignoring.",
416                                                 filename);
417 
418         if (v->independent && v->local_type >= 0)
419                 return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
420                                               "The local address cannot be '%s' when Independent= is enabled, ignoring.",
421                                               strna(netdev_local_address_type_to_string(v->local_type)));
422 
423         return 0;
424 }
425 
netdev_vxlan_is_ready_to_create(NetDev * netdev,Link * link)426 static int netdev_vxlan_is_ready_to_create(NetDev *netdev, Link *link) {
427         VxLan *v;
428 
429         assert(netdev);
430 
431         v = VXLAN(netdev);
432 
433         assert(v);
434 
435         if (v->independent)
436                 return true;
437 
438         return vxlan_get_local_address(v, link, NULL, NULL) >= 0;
439 }
440 
vxlan_init(NetDev * netdev)441 static void vxlan_init(NetDev *netdev) {
442         VxLan *v;
443 
444         assert(netdev);
445 
446         v = VXLAN(netdev);
447 
448         assert(v);
449 
450         v->local_type = _NETDEV_LOCAL_ADDRESS_TYPE_INVALID;
451         v->vni = VXLAN_VID_MAX + 1;
452         v->df = _NETDEV_VXLAN_DF_INVALID;
453         v->learning = true;
454         v->udpcsum = false;
455         v->udp6zerocsumtx = false;
456         v->udp6zerocsumrx = false;
457 }
458 
459 const NetDevVTable vxlan_vtable = {
460         .object_size = sizeof(VxLan),
461         .init = vxlan_init,
462         .sections = NETDEV_COMMON_SECTIONS "VXLAN\0",
463         .fill_message_create = netdev_vxlan_fill_message_create,
464         .create_type = NETDEV_CREATE_STACKED,
465         .is_ready_to_create = netdev_vxlan_is_ready_to_create,
466         .config_verify = netdev_vxlan_verify,
467         .iftype = ARPHRD_ETHER,
468         .generate_mac = true,
469 };
470