chiark / gitweb /
networkd: fdb - refactor a bit
[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         Link *link = userdata;
90         int r;
91
92         assert(link);
93
94         r = sd_rtnl_message_get_errno(m);
95         if (r < 0 && r != -EEXIST)
96                 log_link_error(link, "Could not add FDB entry: %s", strerror(-r));
97
98         return 1;
99 }
100
101 /* send a request to the kernel to add a FDB entry in its static MAC table. */
102 int fdb_entry_configure(Link *link,
103                         FdbEntry *const fdb_entry) {
104         _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
105         sd_rtnl *rtnl;
106         int r;
107
108         assert(link);
109         assert(link->manager);
110         assert(fdb_entry);
111
112         rtnl = link->manager->rtnl;
113
114         /* create new RTM message */
115         r = sd_rtnl_message_new_neigh(rtnl, &req, RTM_NEWNEIGH, link->ifindex, PF_BRIDGE);
116         if (r < 0)
117                 return rtnl_log_create_error(r);
118
119         /* only NTF_SELF flag supported. */
120         r = sd_rtnl_message_neigh_set_flags(req, NTF_SELF);
121         if (r < 0)
122                 return rtnl_log_create_error(r);
123
124         /* only NUD_PERMANENT state supported. */
125         r = sd_rtnl_message_neigh_set_state(req, NUD_NOARP | NUD_PERMANENT);
126         if (r < 0)
127                 return rtnl_log_create_error(r);
128
129         r = sd_rtnl_message_append_ether_addr(req, NDA_LLADDR, fdb_entry->mac_addr);
130         if (r < 0)
131                 return rtnl_log_create_error(r);
132
133         /* VLAN Id is optional. We'll add VLAN Id only if it's specified. */
134         if (0 != fdb_entry->vlan_id) {
135                 r = sd_rtnl_message_append_u16(req, NDA_VLAN, fdb_entry->vlan_id);
136                 if (r < 0)
137                         return rtnl_log_create_error(r);
138         }
139
140         /* send message to the kernel to update its internal static MAC table. */
141         r = sd_rtnl_call_async(rtnl, req, set_fdb_handler, link, 0, NULL);
142         if (r < 0) {
143                 log_link_error(link, "Could not send rtnetlink message: %s", strerror(-r));
144                 return r;
145         }
146
147         return 0;
148 }
149
150 /* remove and FDB entry. */
151 void fdb_entry_free(FdbEntry *fdb_entry) {
152         if(!fdb_entry)
153                 return;
154
155         if(fdb_entry->network) {
156                 LIST_REMOVE(static_fdb_entries, fdb_entry->network->static_fdb_entries,
157                             fdb_entry);
158
159                 if(fdb_entry->section)
160                     hashmap_remove(fdb_entry->network->fdb_entries_by_section,
161                                    UINT_TO_PTR(fdb_entry->section));
162         }
163
164         free(fdb_entry->mac_addr);
165
166         free(fdb_entry);
167 }
168
169 /* parse the HW address from config files. */
170 int config_parse_fdb_hwaddr(const char *unit,
171                             const char *filename,
172                             unsigned line,
173                             const char *section,
174                             unsigned section_line,
175                             const char *lvalue,
176                             int ltype,
177                             const char *rvalue,
178                             void *data,
179                             void *userdata) {
180         Network *network = userdata;
181         _cleanup_fdbentry_free_ FdbEntry *fdb_entry = NULL;
182         int r;
183
184         assert(filename);
185         assert(section);
186         assert(lvalue);
187         assert(rvalue);
188         assert(data);
189
190         r = fdb_entry_new_static(network, section_line, &fdb_entry);
191         if (r < 0) {
192                 log_error("Failed to allocate a new FDB entry: %s", strerror(-r));
193                 return r;
194         }
195
196         /* read in the MAC address for the FDB table. */
197         r = sscanf(rvalue, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
198                    &fdb_entry->mac_addr->ether_addr_octet[0],
199                    &fdb_entry->mac_addr->ether_addr_octet[1],
200                    &fdb_entry->mac_addr->ether_addr_octet[2],
201                    &fdb_entry->mac_addr->ether_addr_octet[3],
202                    &fdb_entry->mac_addr->ether_addr_octet[4],
203                    &fdb_entry->mac_addr->ether_addr_octet[5]);
204
205         if (ETHER_ADDR_LEN !=  r) {
206                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
207                            "Not a valid MAC address, ignoring assignment: %s", rvalue);
208                 return 0;
209         }
210
211         fdb_entry = NULL;
212
213         return 0;
214 }
215
216 /* parse the VLAN Id from config files. */
217 int config_parse_fdb_vlan_id(const char *unit,
218                              const char *filename,
219                              unsigned line,
220                              const char *section,
221                              unsigned section_line,
222                              const char *lvalue,
223                              int ltype,
224                              const char *rvalue,
225                              void *data,
226                              void *userdata) {
227         Network *network = userdata;
228         _cleanup_fdbentry_free_ FdbEntry *fdb_entry = NULL;
229         int r;
230
231         assert(filename);
232         assert(section);
233         assert(lvalue);
234         assert(rvalue);
235         assert(data);
236
237         r = fdb_entry_new_static(network, section_line, &fdb_entry);
238         if (r < 0) {
239                 log_error("Failed to allocate a new FDB entry: %s", strerror(-r));
240                 return r;
241         }
242
243         r = config_parse_unsigned(unit, filename, line, section,
244                                   section_line, lvalue, ltype,
245                                   rvalue, &fdb_entry->vlan_id, userdata);
246         if (r < 0) {
247                 log_error("Failed to parse the unsigned integer: %s", strerror(-r));
248                 return r;
249         }
250
251         fdb_entry = NULL;
252
253         return 0;
254 }