]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-fdb.c
tree-wide: use TAKE_PTR() and TAKE_FD() macros
[thirdparty/systemd.git] / src / network / networkd-fdb.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
b98b483b
AR
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
b98b483b 21#include <net/ethernet.h>
cf0fbc49 22#include <net/if.h>
b98b483b 23
b5efdb8a 24#include "alloc-util.h"
b98b483b 25#include "conf-parser.h"
23f53b99 26#include "netdev/bridge.h"
fc2f9534 27#include "netlink-util.h"
fc2f9534 28#include "networkd-fdb.h"
23f53b99 29#include "networkd-manager.h"
b5efdb8a 30#include "util.h"
0e83e7a5 31#include "vlan-util.h"
b98b483b 32
8c34b963
LP
33#define STATIC_FDB_ENTRIES_PER_NETWORK_MAX 1024U
34
b98b483b 35/* create a new FDB entry or get an existing one. */
8c34b963
LP
36int fdb_entry_new_static(
37 Network *network,
a60a720c 38 unsigned section,
8c34b963
LP
39 FdbEntry **ret) {
40
b98b483b
AR
41 _cleanup_fdbentry_free_ FdbEntry *fdb_entry = NULL;
42 struct ether_addr *mac_addr = NULL;
43
44 assert(network);
8c34b963 45 assert(ret);
b98b483b
AR
46
47 /* search entry in hashmap first. */
9ed794a3 48 if (section) {
b98b483b
AR
49 fdb_entry = hashmap_get(network->fdb_entries_by_section, UINT_TO_PTR(section));
50 if (fdb_entry) {
1cc6c93a 51 *ret = TAKE_PTR(fdb_entry);
b98b483b
AR
52
53 return 0;
54 }
55 }
56
8c34b963
LP
57 if (network->n_static_fdb_entries >= STATIC_FDB_ENTRIES_PER_NETWORK_MAX)
58 return -E2BIG;
59
b98b483b
AR
60 /* allocate space for MAC address. */
61 mac_addr = new0(struct ether_addr, 1);
62 if (!mac_addr)
63 return -ENOMEM;
64
65 /* allocate space for and FDB entry. */
66 fdb_entry = new0(FdbEntry, 1);
b98b483b
AR
67 if (!fdb_entry) {
68 /* free previously allocated space for mac_addr. */
69 free(mac_addr);
70 return -ENOMEM;
71 }
72
73 /* init FDB structure. */
74 fdb_entry->network = network;
75 fdb_entry->mac_addr = mac_addr;
76
77 LIST_PREPEND(static_fdb_entries, network->static_fdb_entries, fdb_entry);
8c34b963 78 network->n_static_fdb_entries++;
b98b483b
AR
79
80 if (section) {
81 fdb_entry->section = section;
82 hashmap_put(network->fdb_entries_by_section,
83 UINT_TO_PTR(fdb_entry->section), fdb_entry);
84 }
85
86 /* return allocated FDB structure. */
1cc6c93a 87 *ret = TAKE_PTR(fdb_entry);
b98b483b
AR
88
89 return 0;
90}
91
1c4baffc 92static int set_fdb_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
ea6ec096 93 Link *link = userdata;
b98b483b
AR
94 int r;
95
ea6ec096 96 assert(link);
b98b483b 97
1c4baffc 98 r = sd_netlink_message_get_errno(m);
ea6ec096 99 if (r < 0 && r != -EEXIST)
6a7a4e4d 100 log_link_error_errno(link, r, "Could not add FDB entry: %m");
b98b483b
AR
101
102 return 1;
103}
104
105/* send a request to the kernel to add a FDB entry in its static MAC table. */
a60a720c 106int fdb_entry_configure(Link *link, FdbEntry *fdb_entry) {
4afd3348 107 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
1c4baffc 108 sd_netlink *rtnl;
b98b483b 109 int r;
f6bb7ac5
TJ
110 uint8_t flags;
111 Bridge *bridge;
b98b483b 112
ea6ec096 113 assert(link);
f6bb7ac5 114 assert(link->network);
ea6ec096 115 assert(link->manager);
b98b483b 116 assert(fdb_entry);
ea6ec096
TG
117
118 rtnl = link->manager->rtnl;
f6bb7ac5 119 bridge = BRIDGE(link->network->bridge);
b98b483b
AR
120
121 /* create new RTM message */
ea6ec096 122 r = sd_rtnl_message_new_neigh(rtnl, &req, RTM_NEWNEIGH, link->ifindex, PF_BRIDGE);
b98b483b
AR
123 if (r < 0)
124 return rtnl_log_create_error(r);
125
f6bb7ac5
TJ
126 if (bridge)
127 flags = NTF_MASTER;
128 else
129 flags = NTF_SELF;
130
131 r = sd_rtnl_message_neigh_set_flags(req, flags);
b98b483b
AR
132 if (r < 0)
133 return rtnl_log_create_error(r);
134
135 /* only NUD_PERMANENT state supported. */
136 r = sd_rtnl_message_neigh_set_state(req, NUD_NOARP | NUD_PERMANENT);
137 if (r < 0)
138 return rtnl_log_create_error(r);
139
1c4baffc 140 r = sd_netlink_message_append_ether_addr(req, NDA_LLADDR, fdb_entry->mac_addr);
b98b483b
AR
141 if (r < 0)
142 return rtnl_log_create_error(r);
143
144 /* VLAN Id is optional. We'll add VLAN Id only if it's specified. */
145 if (0 != fdb_entry->vlan_id) {
1c4baffc 146 r = sd_netlink_message_append_u16(req, NDA_VLAN, fdb_entry->vlan_id);
b98b483b
AR
147 if (r < 0)
148 return rtnl_log_create_error(r);
149 }
150
151 /* send message to the kernel to update its internal static MAC table. */
1c4baffc 152 r = sd_netlink_call_async(rtnl, req, set_fdb_handler, link, 0, NULL);
6a7a4e4d
LP
153 if (r < 0)
154 return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
b98b483b
AR
155
156 return 0;
157}
158
159/* remove and FDB entry. */
160void fdb_entry_free(FdbEntry *fdb_entry) {
9ed794a3 161 if (!fdb_entry)
b98b483b
AR
162 return;
163
9ed794a3 164 if (fdb_entry->network) {
8c34b963
LP
165 LIST_REMOVE(static_fdb_entries, fdb_entry->network->static_fdb_entries, fdb_entry);
166
167 assert(fdb_entry->network->n_static_fdb_entries > 0);
168 fdb_entry->network->n_static_fdb_entries--;
b98b483b 169
8519d8f5 170 if (fdb_entry->section)
8c34b963 171 hashmap_remove(fdb_entry->network->fdb_entries_by_section, UINT_TO_PTR(fdb_entry->section));
b98b483b
AR
172 }
173
174 free(fdb_entry->mac_addr);
175
176 free(fdb_entry);
177}
178
179/* parse the HW address from config files. */
8519d8f5
LP
180int 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
b98b483b
AR
192 Network *network = userdata;
193 _cleanup_fdbentry_free_ 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, section_line, &fdb_entry);
6a7a4e4d
LP
203 if (r < 0)
204 return log_oom();
b98b483b
AR
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
8519d8f5 215 if (ETHER_ADDR_LEN != r) {
12ca818f 216 log_syntax(unit, LOG_ERR, filename, line, 0, "Not a valid MAC address, ignoring assignment: %s", rvalue);
b98b483b
AR
217 return 0;
218 }
219
220 fdb_entry = NULL;
221
222 return 0;
223}
224
225/* parse the VLAN Id from config files. */
8519d8f5
LP
226int 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
b98b483b
AR
238 Network *network = userdata;
239 _cleanup_fdbentry_free_ 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, section_line, &fdb_entry);
6a7a4e4d
LP
249 if (r < 0)
250 return log_oom();
b98b483b 251
0e83e7a5
TJ
252 r = config_parse_vlanid(unit, filename, line, section,
253 section_line, lvalue, ltype,
254 rvalue, &fdb_entry->vlan_id, userdata);
6a7a4e4d 255 if (r < 0)
b98b483b 256 return r;
b98b483b
AR
257
258 fdb_entry = NULL;
259
260 return 0;
261}