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