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