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