]> git.proxmox.com Git - mirror_ovs.git/blob - lib/rtnetlink.c
netdev-offload-tc: Use single 'once' variable for probing tc features
[mirror_ovs.git] / lib / rtnetlink.c
1 /*
2 * Copyright (c) 2009, 2010, 2013, 2015, 2016 Nicira, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at:
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <config.h>
18
19 #include "rtnetlink.h"
20
21 #include <sys/socket.h>
22 #include <linux/rtnetlink.h>
23 #include <net/if.h>
24
25 #include "netlink.h"
26 #include "netlink-notifier.h"
27 #include "openvswitch/ofpbuf.h"
28 #include "packets.h"
29
30 #if IFLA_INFO_MAX < 5
31 #define IFLA_INFO_SLAVE_KIND 4
32 #endif
33
34 static struct nln *nln = NULL;
35 static struct rtnetlink_change rtn_change;
36
37 /* Returns true if the given netlink msg type corresponds to RTNLGRP_LINK. */
38 bool
39 rtnetlink_type_is_rtnlgrp_link(uint16_t type)
40 {
41 return type == RTM_NEWLINK || type == RTM_DELLINK;
42 }
43
44 /* Returns true if the given netlink msg type corresponds to
45 * RTNLGRP_IPV4_IFADDR or RTNLGRP_IPV6_IFADDR. */
46 bool
47 rtnetlink_type_is_rtnlgrp_addr(uint16_t type)
48 {
49 return type == RTM_NEWADDR || type == RTM_DELADDR;
50 }
51
52 /* Parses nested nlattr for link info. Returns false if unparseable, else
53 * populates 'change' and returns true. */
54 static bool
55 rtnetlink_parse_link_info(const struct nlattr *nla,
56 struct rtnetlink_change *change)
57 {
58 bool parsed = false;
59
60 static const struct nl_policy linkinfo_policy[] = {
61 [IFLA_INFO_KIND] = { .type = NL_A_STRING, .optional = true },
62 [IFLA_INFO_SLAVE_KIND] = { .type = NL_A_STRING, .optional = true },
63 };
64
65 struct nlattr *linkinfo[ARRAY_SIZE(linkinfo_policy)];
66
67 parsed = nl_parse_nested(nla, linkinfo_policy, linkinfo,
68 ARRAY_SIZE(linkinfo_policy));
69
70 if (parsed) {
71 change->primary = (linkinfo[IFLA_INFO_KIND]
72 ? nl_attr_get_string(linkinfo[IFLA_INFO_KIND])
73 : NULL);
74 change->sub = (linkinfo[IFLA_INFO_SLAVE_KIND]
75 ? nl_attr_get_string(linkinfo[IFLA_INFO_SLAVE_KIND])
76 : NULL);
77 }
78
79 return parsed;
80 }
81
82 /* Parses a rtnetlink message 'buf' into 'change'. If 'buf' is unparseable,
83 * leaves 'change' untouched and returns false. Otherwise, populates 'change'
84 * and returns true. */
85 bool
86 rtnetlink_parse(struct ofpbuf *buf, struct rtnetlink_change *change)
87 {
88 const struct nlmsghdr *nlmsg = buf->data;
89 bool parsed = false;
90
91 if (rtnetlink_type_is_rtnlgrp_link(nlmsg->nlmsg_type)) {
92 /* Policy for RTNLGRP_LINK messages.
93 *
94 * There are *many* more fields in these messages, but currently we
95 * only care about these fields. */
96 static const struct nl_policy policy[] = {
97 [IFLA_IFNAME] = { .type = NL_A_STRING, .optional = false },
98 [IFLA_MASTER] = { .type = NL_A_U32, .optional = true },
99 [IFLA_MTU] = { .type = NL_A_U32, .optional = true },
100 [IFLA_ADDRESS] = { .type = NL_A_UNSPEC, .optional = true },
101 [IFLA_LINKINFO] = { .type = NL_A_NESTED, .optional = true },
102 };
103
104 struct nlattr *attrs[ARRAY_SIZE(policy)];
105
106 parsed = nl_policy_parse(buf, NLMSG_HDRLEN + sizeof(struct ifinfomsg),
107 policy, attrs, ARRAY_SIZE(policy));
108
109 if (parsed) {
110 const struct ifinfomsg *ifinfo;
111
112 ifinfo = ofpbuf_at(buf, NLMSG_HDRLEN, sizeof *ifinfo);
113
114 change->nlmsg_type = nlmsg->nlmsg_type;
115 change->if_index = ifinfo->ifi_index;
116 change->ifname = nl_attr_get_string(attrs[IFLA_IFNAME]);
117 change->ifi_flags = ifinfo->ifi_flags;
118 change->master_ifindex = (attrs[IFLA_MASTER]
119 ? nl_attr_get_u32(attrs[IFLA_MASTER])
120 : 0);
121 change->mtu = (attrs[IFLA_MTU]
122 ? nl_attr_get_u32(attrs[IFLA_MTU])
123 : 0);
124
125 if (attrs[IFLA_ADDRESS] &&
126 nl_attr_get_size(attrs[IFLA_ADDRESS]) == ETH_ADDR_LEN) {
127 memcpy(&change->mac, nl_attr_get(attrs[IFLA_ADDRESS]),
128 ETH_ADDR_LEN);
129 } else {
130 memset(&change->mac, 0, ETH_ADDR_LEN);
131 }
132
133 if (attrs[IFLA_LINKINFO]) {
134 parsed = rtnetlink_parse_link_info(attrs[IFLA_LINKINFO],
135 change);
136 } else {
137 change->primary = NULL;
138 change->sub = NULL;
139 }
140 }
141 } else if (rtnetlink_type_is_rtnlgrp_addr(nlmsg->nlmsg_type)) {
142 /* Policy for RTNLGRP_IPV4_IFADDR/RTNLGRP_IPV6_IFADDR messages.
143 *
144 * There are *many* more fields in these messages, but currently we
145 * only care about these fields. */
146 static const struct nl_policy policy[] = {
147 [IFA_LABEL] = { .type = NL_A_STRING, .optional = true },
148 };
149
150 struct nlattr *attrs[ARRAY_SIZE(policy)];
151
152 parsed = nl_policy_parse(buf, NLMSG_HDRLEN + sizeof(struct ifaddrmsg),
153 policy, attrs, ARRAY_SIZE(policy));
154
155 if (parsed) {
156 const struct ifaddrmsg *ifaddr;
157
158 ifaddr = ofpbuf_at(buf, NLMSG_HDRLEN, sizeof *ifaddr);
159
160 change->nlmsg_type = nlmsg->nlmsg_type;
161 change->if_index = ifaddr->ifa_index;
162 change->ifname = (attrs[IFA_LABEL]
163 ? nl_attr_get_string(attrs[IFA_LABEL])
164 : NULL);
165 }
166 }
167
168 return parsed;
169 }
170
171 /* Return RTNLGRP_LINK on success, 0 on parse error. */
172 static int
173 rtnetlink_parse_cb(struct ofpbuf *buf, void *change)
174 {
175 return rtnetlink_parse(buf, change) ? RTNLGRP_LINK : 0;
176 }
177
178 /* Registers 'cb' to be called with auxiliary data 'aux' with network device
179 * change notifications. The notifier is stored in 'notifier', which the
180 * caller must not modify or free.
181 *
182 * This is probably not the function that you want. You should probably be
183 * using dpif_port_poll() or netdev_change_seq(), which unlike this function
184 * are not Linux-specific.
185 *
186 * xxx Joins more multicast groups when needed.
187 *
188 * Returns an initialized nln_notifier if successful, NULL otherwise. */
189 struct nln_notifier *
190 rtnetlink_notifier_create(rtnetlink_notify_func *cb, void *aux)
191 {
192 if (!nln) {
193 nln = nln_create(NETLINK_ROUTE, rtnetlink_parse_cb, &rtn_change);
194 }
195
196 return nln_notifier_create(nln, RTNLGRP_LINK, (nln_notify_func *) cb, aux);
197 }
198
199 /* Destroys 'notifier', which must have previously been created with
200 * rtnetlink_notifier_register(). */
201 void
202 rtnetlink_notifier_destroy(struct nln_notifier *notifier)
203 {
204 nln_notifier_destroy(notifier);
205 }
206
207 /* Calls all of the registered notifiers, passing along any as-yet-unreported
208 * netdev change events. */
209 void
210 rtnetlink_run(void)
211 {
212 if (nln) {
213 nln_run(nln);
214 }
215 }
216
217 /* Causes poll_block() to wake up when network device change notifications are
218 * ready. */
219 void
220 rtnetlink_wait(void)
221 {
222 if (nln) {
223 nln_wait(nln);
224 }
225 }
226
227 /* Report RTNLGRP_LINK netdev change events. */
228 void
229 rtnetlink_report_link(void)
230 {
231 if (nln) {
232 nln_report(nln, NULL, RTNLGRP_LINK);
233 }
234 }