]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-fdb.c
Merge pull request #10984 from fbuihuu/tmpfiles-be-more-explicit-with-unsafe-transition
[thirdparty/systemd.git] / src / network / networkd-fdb.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 Copyright © 2014 Intel Corporation. All rights reserved.
4 ***/
5
6 #include <net/ethernet.h>
7 #include <net/if.h>
8
9 #include "alloc-util.h"
10 #include "conf-parser.h"
11 #include "netdev/bridge.h"
12 #include "netlink-util.h"
13 #include "networkd-fdb.h"
14 #include "networkd-manager.h"
15 #include "util.h"
16 #include "vlan-util.h"
17
18 #define STATIC_FDB_ENTRIES_PER_NETWORK_MAX 1024U
19
20 /* create a new FDB entry or get an existing one. */
21 int fdb_entry_new_static(
22 Network *network,
23 const char *filename,
24 unsigned section_line,
25 FdbEntry **ret) {
26
27 _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
28 _cleanup_(fdb_entry_freep) FdbEntry *fdb_entry = NULL;
29 _cleanup_free_ struct ether_addr *mac_addr = NULL;
30 int r;
31
32 assert(network);
33 assert(ret);
34 assert(!!filename == (section_line > 0));
35
36 /* search entry in hashmap first. */
37 if (filename) {
38 r = network_config_section_new(filename, section_line, &n);
39 if (r < 0)
40 return r;
41
42 fdb_entry = hashmap_get(network->fdb_entries_by_section, n);
43 if (fdb_entry) {
44 *ret = TAKE_PTR(fdb_entry);
45
46 return 0;
47 }
48 }
49
50 if (network->n_static_fdb_entries >= STATIC_FDB_ENTRIES_PER_NETWORK_MAX)
51 return -E2BIG;
52
53 /* allocate space for MAC address. */
54 mac_addr = new0(struct ether_addr, 1);
55 if (!mac_addr)
56 return -ENOMEM;
57
58 /* allocate space for and FDB entry. */
59 fdb_entry = new(FdbEntry, 1);
60 if (!fdb_entry)
61 return -ENOMEM;
62
63 /* init FDB structure. */
64 *fdb_entry = (FdbEntry) {
65 .network = network,
66 .mac_addr = TAKE_PTR(mac_addr),
67 };
68
69 LIST_PREPEND(static_fdb_entries, network->static_fdb_entries, fdb_entry);
70 network->n_static_fdb_entries++;
71
72 if (filename) {
73 fdb_entry->section = TAKE_PTR(n);
74
75 r = hashmap_ensure_allocated(&network->fdb_entries_by_section, &network_config_hash_ops);
76 if (r < 0)
77 return r;
78
79 r = hashmap_put(network->fdb_entries_by_section, fdb_entry->section, fdb_entry);
80 if (r < 0)
81 return r;
82 }
83
84 /* return allocated FDB structure. */
85 *ret = TAKE_PTR(fdb_entry);
86
87 return 0;
88 }
89
90 static int set_fdb_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
91 int r;
92
93 assert(link);
94
95 r = sd_netlink_message_get_errno(m);
96 if (r < 0 && r != -EEXIST)
97 log_link_error_errno(link, r, "Could not add FDB entry: %m");
98
99 return 1;
100 }
101
102 /* send a request to the kernel to add a FDB entry in its static MAC table. */
103 int fdb_entry_configure(Link *link, FdbEntry *fdb_entry) {
104 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
105 sd_netlink *rtnl;
106 int r;
107 uint8_t flags;
108 Bridge *bridge;
109
110 assert(link);
111 assert(link->network);
112 assert(link->manager);
113 assert(fdb_entry);
114
115 rtnl = link->manager->rtnl;
116 bridge = BRIDGE(link->network->bridge);
117
118 /* create new RTM message */
119 r = sd_rtnl_message_new_neigh(rtnl, &req, RTM_NEWNEIGH, link->ifindex, PF_BRIDGE);
120 if (r < 0)
121 return rtnl_log_create_error(r);
122
123 if (bridge)
124 flags = NTF_MASTER;
125 else
126 flags = NTF_SELF;
127
128 r = sd_rtnl_message_neigh_set_flags(req, flags);
129 if (r < 0)
130 return rtnl_log_create_error(r);
131
132 /* only NUD_PERMANENT state supported. */
133 r = sd_rtnl_message_neigh_set_state(req, NUD_NOARP | NUD_PERMANENT);
134 if (r < 0)
135 return rtnl_log_create_error(r);
136
137 r = sd_netlink_message_append_ether_addr(req, NDA_LLADDR, fdb_entry->mac_addr);
138 if (r < 0)
139 return rtnl_log_create_error(r);
140
141 /* VLAN Id is optional. We'll add VLAN Id only if it's specified. */
142 if (0 != fdb_entry->vlan_id) {
143 r = sd_netlink_message_append_u16(req, NDA_VLAN, fdb_entry->vlan_id);
144 if (r < 0)
145 return rtnl_log_create_error(r);
146 }
147
148 /* send message to the kernel to update its internal static MAC table. */
149 r = netlink_call_async(rtnl, NULL, req, set_fdb_handler,
150 link_netlink_destroy_callback, link);
151 if (r < 0)
152 return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
153
154 link_ref(link);
155
156 return 0;
157 }
158
159 /* remove and FDB entry. */
160 void fdb_entry_free(FdbEntry *fdb_entry) {
161 if (!fdb_entry)
162 return;
163
164 if (fdb_entry->network) {
165 LIST_REMOVE(static_fdb_entries, fdb_entry->network->static_fdb_entries, fdb_entry);
166 assert(fdb_entry->network->n_static_fdb_entries > 0);
167 fdb_entry->network->n_static_fdb_entries--;
168
169 if (fdb_entry->section)
170 hashmap_remove(fdb_entry->network->fdb_entries_by_section, fdb_entry->section);
171 }
172
173 network_config_section_free(fdb_entry->section);
174 free(fdb_entry->mac_addr);
175 free(fdb_entry);
176 }
177
178 /* parse the HW address from config files. */
179 int config_parse_fdb_hwaddr(
180 const char *unit,
181 const char *filename,
182 unsigned line,
183 const char *section,
184 unsigned section_line,
185 const char *lvalue,
186 int ltype,
187 const char *rvalue,
188 void *data,
189 void *userdata) {
190
191 Network *network = userdata;
192 _cleanup_(fdb_entry_freep) FdbEntry *fdb_entry = NULL;
193 int r;
194
195 assert(filename);
196 assert(section);
197 assert(lvalue);
198 assert(rvalue);
199 assert(data);
200
201 r = fdb_entry_new_static(network, filename, section_line, &fdb_entry);
202 if (r < 0)
203 return log_oom();
204
205 /* read in the MAC address for the FDB table. */
206 r = sscanf(rvalue, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
207 &fdb_entry->mac_addr->ether_addr_octet[0],
208 &fdb_entry->mac_addr->ether_addr_octet[1],
209 &fdb_entry->mac_addr->ether_addr_octet[2],
210 &fdb_entry->mac_addr->ether_addr_octet[3],
211 &fdb_entry->mac_addr->ether_addr_octet[4],
212 &fdb_entry->mac_addr->ether_addr_octet[5]);
213
214 if (r != ETHER_ADDR_LEN) {
215 log_syntax(unit, LOG_ERR, filename, line, 0, "Not a valid MAC address, ignoring assignment: %s", rvalue);
216 return 0;
217 }
218
219 fdb_entry = NULL;
220
221 return 0;
222 }
223
224 /* parse the VLAN Id from config files. */
225 int config_parse_fdb_vlan_id(
226 const char *unit,
227 const char *filename,
228 unsigned line,
229 const char *section,
230 unsigned section_line,
231 const char *lvalue,
232 int ltype,
233 const char *rvalue,
234 void *data,
235 void *userdata) {
236
237 Network *network = userdata;
238 _cleanup_(fdb_entry_freep) FdbEntry *fdb_entry = NULL;
239 int r;
240
241 assert(filename);
242 assert(section);
243 assert(lvalue);
244 assert(rvalue);
245 assert(data);
246
247 r = fdb_entry_new_static(network, filename, section_line, &fdb_entry);
248 if (r < 0)
249 return log_oom();
250
251 r = config_parse_vlanid(unit, filename, line, section,
252 section_line, lvalue, ltype,
253 rvalue, &fdb_entry->vlan_id, userdata);
254 if (r < 0)
255 return r;
256
257 fdb_entry = NULL;
258
259 return 0;
260 }