chiark / gitweb /
networkd: add FDB support
[elogind.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 <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. */
36 int 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
88 static 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. */
103 int 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. */
149 void 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. */
168 int 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. */
215 int 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 }