]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-fdb.c
network: allocate hashmap objects when they are required
[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, void *userdata) {
91 Link *link = userdata;
92 int r;
93
94 assert(link);
95
96 r = sd_netlink_message_get_errno(m);
97 if (r < 0 && r != -EEXIST)
98 log_link_error_errno(link, r, "Could not add FDB entry: %m");
99
100 return 1;
101 }
102
103 /* send a request to the kernel to add a FDB entry in its static MAC table. */
104 int fdb_entry_configure(Link *link, FdbEntry *fdb_entry) {
105 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
106 sd_netlink *rtnl;
107 int r;
108 uint8_t flags;
109 Bridge *bridge;
110
111 assert(link);
112 assert(link->network);
113 assert(link->manager);
114 assert(fdb_entry);
115
116 rtnl = link->manager->rtnl;
117 bridge = BRIDGE(link->network->bridge);
118
119 /* create new RTM message */
120 r = sd_rtnl_message_new_neigh(rtnl, &req, RTM_NEWNEIGH, link->ifindex, PF_BRIDGE);
121 if (r < 0)
122 return rtnl_log_create_error(r);
123
124 if (bridge)
125 flags = NTF_MASTER;
126 else
127 flags = NTF_SELF;
128
129 r = sd_rtnl_message_neigh_set_flags(req, flags);
130 if (r < 0)
131 return rtnl_log_create_error(r);
132
133 /* only NUD_PERMANENT state supported. */
134 r = sd_rtnl_message_neigh_set_state(req, NUD_NOARP | NUD_PERMANENT);
135 if (r < 0)
136 return rtnl_log_create_error(r);
137
138 r = sd_netlink_message_append_ether_addr(req, NDA_LLADDR, fdb_entry->mac_addr);
139 if (r < 0)
140 return rtnl_log_create_error(r);
141
142 /* VLAN Id is optional. We'll add VLAN Id only if it's specified. */
143 if (0 != fdb_entry->vlan_id) {
144 r = sd_netlink_message_append_u16(req, NDA_VLAN, fdb_entry->vlan_id);
145 if (r < 0)
146 return rtnl_log_create_error(r);
147 }
148
149 /* send message to the kernel to update its internal static MAC table. */
150 r = sd_netlink_call_async(rtnl, NULL, req, set_fdb_handler,
151 link_netlink_destroy_callback, link, 0, __func__);
152 if (r < 0)
153 return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
154
155 link_ref(link);
156
157 return 0;
158 }
159
160 /* remove and FDB entry. */
161 void fdb_entry_free(FdbEntry *fdb_entry) {
162 if (!fdb_entry)
163 return;
164
165 if (fdb_entry->network) {
166 LIST_REMOVE(static_fdb_entries, fdb_entry->network->static_fdb_entries, fdb_entry);
167 assert(fdb_entry->network->n_static_fdb_entries > 0);
168 fdb_entry->network->n_static_fdb_entries--;
169
170 if (fdb_entry->section)
171 hashmap_remove(fdb_entry->network->fdb_entries_by_section, fdb_entry->section);
172 }
173
174 network_config_section_free(fdb_entry->section);
175 free(fdb_entry->mac_addr);
176 free(fdb_entry);
177 }
178
179 /* parse the HW address from config files. */
180 int config_parse_fdb_hwaddr(
181 const char *unit,
182 const char *filename,
183 unsigned line,
184 const char *section,
185 unsigned section_line,
186 const char *lvalue,
187 int ltype,
188 const char *rvalue,
189 void *data,
190 void *userdata) {
191
192 Network *network = userdata;
193 _cleanup_(fdb_entry_freep) FdbEntry *fdb_entry = NULL;
194 int r;
195
196 assert(filename);
197 assert(section);
198 assert(lvalue);
199 assert(rvalue);
200 assert(data);
201
202 r = fdb_entry_new_static(network, filename, section_line, &fdb_entry);
203 if (r < 0)
204 return log_oom();
205
206 /* read in the MAC address for the FDB table. */
207 r = sscanf(rvalue, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
208 &fdb_entry->mac_addr->ether_addr_octet[0],
209 &fdb_entry->mac_addr->ether_addr_octet[1],
210 &fdb_entry->mac_addr->ether_addr_octet[2],
211 &fdb_entry->mac_addr->ether_addr_octet[3],
212 &fdb_entry->mac_addr->ether_addr_octet[4],
213 &fdb_entry->mac_addr->ether_addr_octet[5]);
214
215 if (r != ETHER_ADDR_LEN) {
216 log_syntax(unit, LOG_ERR, filename, line, 0, "Not a valid MAC address, ignoring assignment: %s", rvalue);
217 return 0;
218 }
219
220 fdb_entry = NULL;
221
222 return 0;
223 }
224
225 /* parse the VLAN Id from config files. */
226 int config_parse_fdb_vlan_id(
227 const char *unit,
228 const char *filename,
229 unsigned line,
230 const char *section,
231 unsigned section_line,
232 const char *lvalue,
233 int ltype,
234 const char *rvalue,
235 void *data,
236 void *userdata) {
237
238 Network *network = userdata;
239 _cleanup_(fdb_entry_freep) FdbEntry *fdb_entry = NULL;
240 int r;
241
242 assert(filename);
243 assert(section);
244 assert(lvalue);
245 assert(rvalue);
246 assert(data);
247
248 r = fdb_entry_new_static(network, filename, section_line, &fdb_entry);
249 if (r < 0)
250 return log_oom();
251
252 r = config_parse_vlanid(unit, filename, line, section,
253 section_line, lvalue, ltype,
254 rvalue, &fdb_entry->vlan_id, userdata);
255 if (r < 0)
256 return r;
257
258 fdb_entry = NULL;
259
260 return 0;
261 }