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