1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include <errno.h>
4 #include <net/if.h>
5 #include <linux/if_arp.h>
6 #include <linux/if_vlan.h>
7 
8 #include "parse-util.h"
9 #include "vlan-util.h"
10 #include "vlan.h"
11 
netdev_vlan_fill_message_create(NetDev * netdev,Link * link,sd_netlink_message * req)12 static int netdev_vlan_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *req) {
13         struct ifla_vlan_flags flags = {};
14         VLan *v;
15         int r;
16 
17         assert(netdev);
18         assert(link);
19         assert(req);
20 
21         v = VLAN(netdev);
22 
23         assert(v);
24 
25         r = sd_netlink_message_append_u16(req, IFLA_VLAN_ID, v->id);
26         if (r < 0)
27                 return r;
28 
29         if (v->protocol >= 0) {
30                 r = sd_netlink_message_append_u16(req, IFLA_VLAN_PROTOCOL, htobe16(v->protocol));
31                 if (r < 0)
32                         return r;
33         }
34 
35         if (v->gvrp != -1) {
36                 flags.mask |= VLAN_FLAG_GVRP;
37                 SET_FLAG(flags.flags, VLAN_FLAG_GVRP, v->gvrp);
38         }
39 
40         if (v->mvrp != -1) {
41                 flags.mask |= VLAN_FLAG_MVRP;
42                 SET_FLAG(flags.flags, VLAN_FLAG_MVRP, v->mvrp);
43         }
44 
45         if (v->reorder_hdr != -1) {
46                 flags.mask |= VLAN_FLAG_REORDER_HDR;
47                 SET_FLAG(flags.flags, VLAN_FLAG_REORDER_HDR, v->reorder_hdr);
48         }
49 
50         if (v->loose_binding != -1) {
51                 flags.mask |= VLAN_FLAG_LOOSE_BINDING;
52                 SET_FLAG(flags.flags, VLAN_FLAG_LOOSE_BINDING, v->loose_binding);
53         }
54 
55         r = sd_netlink_message_append_data(req, IFLA_VLAN_FLAGS, &flags, sizeof(struct ifla_vlan_flags));
56         if (r < 0)
57                 return r;
58 
59         if (!set_isempty(v->egress_qos_maps)) {
60                 struct ifla_vlan_qos_mapping *m;
61 
62                 r = sd_netlink_message_open_container(req, IFLA_VLAN_EGRESS_QOS);
63                 if (r < 0)
64                         return r;
65 
66                 SET_FOREACH(m, v->egress_qos_maps) {
67                         r = sd_netlink_message_append_data(req, IFLA_VLAN_QOS_MAPPING, m, sizeof(struct ifla_vlan_qos_mapping));
68                         if (r < 0)
69                                 return r;
70                 }
71 
72                 r = sd_netlink_message_close_container(req);
73                 if (r < 0)
74                         return r;
75         }
76 
77         if (!set_isempty(v->ingress_qos_maps)) {
78                 struct ifla_vlan_qos_mapping *m;
79 
80                 r = sd_netlink_message_open_container(req, IFLA_VLAN_INGRESS_QOS);
81                 if (r < 0)
82                         return r;
83 
84                 SET_FOREACH(m, v->ingress_qos_maps) {
85                         r = sd_netlink_message_append_data(req, IFLA_VLAN_QOS_MAPPING, m, sizeof(struct ifla_vlan_qos_mapping));
86                         if (r < 0)
87                                 return r;
88                 }
89 
90                 r = sd_netlink_message_close_container(req);
91                 if (r < 0)
92                         return r;
93         }
94 
95         return 0;
96 }
97 
vlan_qos_maps_hash_func(const struct ifla_vlan_qos_mapping * x,struct siphash * state)98 static void vlan_qos_maps_hash_func(const struct ifla_vlan_qos_mapping *x, struct siphash *state) {
99         siphash24_compress(&x->from, sizeof(x->from), state);
100         siphash24_compress(&x->to, sizeof(x->to), state);
101 }
102 
vlan_qos_maps_compare_func(const struct ifla_vlan_qos_mapping * a,const struct ifla_vlan_qos_mapping * b)103 static int vlan_qos_maps_compare_func(const struct ifla_vlan_qos_mapping *a, const struct ifla_vlan_qos_mapping *b) {
104         int r;
105 
106         r = CMP(a->from, b->from);
107         if (r != 0)
108                 return r;
109 
110         return CMP(a->to, b->to);
111 }
112 
113 DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
114                 vlan_qos_maps_hash_ops,
115                 struct ifla_vlan_qos_mapping,
116                 vlan_qos_maps_hash_func,
117                 vlan_qos_maps_compare_func,
118                 free);
119 
config_parse_vlan_qos_maps(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)120 int config_parse_vlan_qos_maps(
121                 const char *unit,
122                 const char *filename,
123                 unsigned line,
124                 const char *section,
125                 unsigned section_line,
126                 const char *lvalue,
127                 int ltype,
128                 const char *rvalue,
129                 void *data,
130                 void *userdata) {
131 
132         Set **s = data;
133         int r;
134 
135         assert(filename);
136         assert(lvalue);
137         assert(rvalue);
138         assert(data);
139 
140         if (isempty(rvalue)) {
141                 *s = set_free(*s);
142                 return 0;
143         }
144 
145         for (const char *p = rvalue;;) {
146                 _cleanup_free_ struct ifla_vlan_qos_mapping *m = NULL;
147                 _cleanup_free_ char *w = NULL;
148 
149                 r = extract_first_word(&p, &w, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNQUOTE);
150                 if (r == -ENOMEM)
151                         return log_oom();
152                 if (r < 0) {
153                         log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse %s, ignoring: %s", lvalue, rvalue);
154                         return 0;
155                 }
156                 if (r == 0)
157                         return 0;
158 
159                 m = new0(struct ifla_vlan_qos_mapping, 1);
160                 if (!m)
161                         return log_oom();
162 
163                 r = parse_range(w, &m->from, &m->to);
164                 if (r < 0) {
165                         log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse %s, ignoring: %s", lvalue, w);
166                         continue;
167                 }
168 
169                 if (m->to > m->from || m->to == 0 || m->from == 0) {
170                         log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid %s, ignoring: %s", lvalue, w);
171                         continue;
172                 }
173 
174                 r = set_ensure_consume(s, &vlan_qos_maps_hash_ops, TAKE_PTR(m));
175                 if (r < 0) {
176                         log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to store %s, ignoring: %s", lvalue, w);
177                         continue;
178                 }
179         }
180 }
181 
netdev_vlan_verify(NetDev * netdev,const char * filename)182 static int netdev_vlan_verify(NetDev *netdev, const char *filename) {
183         VLan *v;
184 
185         assert(netdev);
186         assert(filename);
187 
188         v = VLAN(netdev);
189 
190         assert(v);
191 
192         if (v->id == VLANID_INVALID) {
193                 log_netdev_warning(netdev, "VLAN without valid Id (%"PRIu16") configured in %s.", v->id, filename);
194                 return -EINVAL;
195         }
196 
197         return 0;
198 }
199 
vlan_done(NetDev * n)200 static void vlan_done(NetDev *n) {
201         VLan *v;
202 
203         v = VLAN(n);
204 
205         assert(v);
206 
207         set_free(v->egress_qos_maps);
208         set_free(v->ingress_qos_maps);
209 }
210 
vlan_init(NetDev * netdev)211 static void vlan_init(NetDev *netdev) {
212         VLan *v = VLAN(netdev);
213 
214         assert(netdev);
215         assert(v);
216 
217         v->id = VLANID_INVALID;
218         v->protocol = -1;
219         v->gvrp = -1;
220         v->mvrp = -1;
221         v->loose_binding = -1;
222         v->reorder_hdr = -1;
223 }
224 
225 const NetDevVTable vlan_vtable = {
226         .object_size = sizeof(VLan),
227         .init = vlan_init,
228         .sections = NETDEV_COMMON_SECTIONS "VLAN\0",
229         .fill_message_create = netdev_vlan_fill_message_create,
230         .create_type = NETDEV_CREATE_STACKED,
231         .config_verify = netdev_vlan_verify,
232         .done = vlan_done,
233         .iftype = ARPHRD_ETHER,
234 };
235