]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-fdb.c
Merge pull request #154 from dmedri/master
[thirdparty/systemd.git] / src / network / networkd-fdb.c
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 <net/if.h>
23 #include <net/ethernet.h>
24
25 #include "networkd.h"
26 #include "networkd-link.h"
27 #include "conf-parser.h"
28 #include "util.h"
29
30 /* create a new FDB entry or get an existing one. */
31 int fdb_entry_new_static(Network *const network,
32 const unsigned section,
33 FdbEntry **ret) {
34 _cleanup_fdbentry_free_ FdbEntry *fdb_entry = NULL;
35 struct ether_addr *mac_addr = NULL;
36
37 assert(network);
38
39 /* search entry in hashmap first. */
40 if(section) {
41 fdb_entry = hashmap_get(network->fdb_entries_by_section, UINT_TO_PTR(section));
42 if (fdb_entry) {
43 *ret = fdb_entry;
44 fdb_entry = NULL;
45
46 return 0;
47 }
48 }
49
50 /* allocate space for MAC address. */
51 mac_addr = new0(struct ether_addr, 1);
52 if (!mac_addr)
53 return -ENOMEM;
54
55 /* allocate space for and FDB entry. */
56 fdb_entry = new0(FdbEntry, 1);
57
58 if (!fdb_entry) {
59 /* free previously allocated space for mac_addr. */
60 free(mac_addr);
61 return -ENOMEM;
62 }
63
64 /* init FDB structure. */
65 fdb_entry->network = network;
66 fdb_entry->mac_addr = mac_addr;
67
68 LIST_PREPEND(static_fdb_entries, network->static_fdb_entries, fdb_entry);
69
70 if (section) {
71 fdb_entry->section = section;
72 hashmap_put(network->fdb_entries_by_section,
73 UINT_TO_PTR(fdb_entry->section), fdb_entry);
74 }
75
76 /* return allocated FDB structure. */
77 *ret = fdb_entry;
78 fdb_entry = NULL;
79
80 return 0;
81 }
82
83 static int set_fdb_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
84 Link *link = userdata;
85 int r;
86
87 assert(link);
88
89 r = sd_netlink_message_get_errno(m);
90 if (r < 0 && r != -EEXIST)
91 log_link_error_errno(link, r, "Could not add FDB entry: %m");
92
93 return 1;
94 }
95
96 /* send a request to the kernel to add a FDB entry in its static MAC table. */
97 int fdb_entry_configure(Link *const link, FdbEntry *const fdb_entry) {
98 _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL;
99 sd_netlink *rtnl;
100 int r;
101
102 assert(link);
103 assert(link->manager);
104 assert(fdb_entry);
105
106 rtnl = link->manager->rtnl;
107
108 /* create new RTM message */
109 r = sd_rtnl_message_new_neigh(rtnl, &req, RTM_NEWNEIGH, link->ifindex, PF_BRIDGE);
110 if (r < 0)
111 return rtnl_log_create_error(r);
112
113 /* only NTF_SELF flag supported. */
114 r = sd_rtnl_message_neigh_set_flags(req, NTF_SELF);
115 if (r < 0)
116 return rtnl_log_create_error(r);
117
118 /* only NUD_PERMANENT state supported. */
119 r = sd_rtnl_message_neigh_set_state(req, NUD_NOARP | NUD_PERMANENT);
120 if (r < 0)
121 return rtnl_log_create_error(r);
122
123 r = sd_netlink_message_append_ether_addr(req, NDA_LLADDR, fdb_entry->mac_addr);
124 if (r < 0)
125 return rtnl_log_create_error(r);
126
127 /* VLAN Id is optional. We'll add VLAN Id only if it's specified. */
128 if (0 != fdb_entry->vlan_id) {
129 r = sd_netlink_message_append_u16(req, NDA_VLAN, fdb_entry->vlan_id);
130 if (r < 0)
131 return rtnl_log_create_error(r);
132 }
133
134 /* send message to the kernel to update its internal static MAC table. */
135 r = sd_netlink_call_async(rtnl, req, set_fdb_handler, link, 0, NULL);
136 if (r < 0)
137 return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
138
139 return 0;
140 }
141
142 /* remove and FDB entry. */
143 void fdb_entry_free(FdbEntry *fdb_entry) {
144 if(!fdb_entry)
145 return;
146
147 if(fdb_entry->network) {
148 LIST_REMOVE(static_fdb_entries, fdb_entry->network->static_fdb_entries,
149 fdb_entry);
150
151 if (fdb_entry->section)
152 hashmap_remove(fdb_entry->network->fdb_entries_by_section,
153 UINT_TO_PTR(fdb_entry->section));
154 }
155
156 free(fdb_entry->mac_addr);
157
158 free(fdb_entry);
159 }
160
161 /* parse the HW address from config files. */
162 int config_parse_fdb_hwaddr(
163 const char *unit,
164 const char *filename,
165 unsigned line,
166 const char *section,
167 unsigned section_line,
168 const char *lvalue,
169 int ltype,
170 const char *rvalue,
171 void *data,
172 void *userdata) {
173
174 Network *network = userdata;
175 _cleanup_fdbentry_free_ FdbEntry *fdb_entry = NULL;
176 int r;
177
178 assert(filename);
179 assert(section);
180 assert(lvalue);
181 assert(rvalue);
182 assert(data);
183
184 r = fdb_entry_new_static(network, section_line, &fdb_entry);
185 if (r < 0)
186 return log_oom();
187
188 /* read in the MAC address for the FDB table. */
189 r = sscanf(rvalue, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
190 &fdb_entry->mac_addr->ether_addr_octet[0],
191 &fdb_entry->mac_addr->ether_addr_octet[1],
192 &fdb_entry->mac_addr->ether_addr_octet[2],
193 &fdb_entry->mac_addr->ether_addr_octet[3],
194 &fdb_entry->mac_addr->ether_addr_octet[4],
195 &fdb_entry->mac_addr->ether_addr_octet[5]);
196
197 if (ETHER_ADDR_LEN != r) {
198 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Not a valid MAC address, ignoring assignment: %s", rvalue);
199 return 0;
200 }
201
202 fdb_entry = NULL;
203
204 return 0;
205 }
206
207 /* parse the VLAN Id from config files. */
208 int config_parse_fdb_vlan_id(
209 const char *unit,
210 const char *filename,
211 unsigned line,
212 const char *section,
213 unsigned section_line,
214 const char *lvalue,
215 int ltype,
216 const char *rvalue,
217 void *data,
218 void *userdata) {
219
220 Network *network = userdata;
221 _cleanup_fdbentry_free_ FdbEntry *fdb_entry = NULL;
222 int r;
223
224 assert(filename);
225 assert(section);
226 assert(lvalue);
227 assert(rvalue);
228 assert(data);
229
230 r = fdb_entry_new_static(network, section_line, &fdb_entry);
231 if (r < 0)
232 return log_oom();
233
234 r = config_parse_unsigned(unit, filename, line, section,
235 section_line, lvalue, ltype,
236 rvalue, &fdb_entry->vlan_id, userdata);
237 if (r < 0)
238 return r;
239
240 fdb_entry = NULL;
241
242 return 0;
243 }