]> git.proxmox.com Git - mirror_ovs.git/blame - lib/rtnetlink.c
ovsdb-idl: Fix iteration over tracked rows with no actual data.
[mirror_ovs.git] / lib / rtnetlink.c
CommitLineData
7e9dcc0f 1/*
a79ba858 2 * Copyright (c) 2009, 2010, 2013, 2015, 2016 Nicira, Inc.
7e9dcc0f
AW
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"
64c96779 27#include "openvswitch/ofpbuf.h"
a79ba858 28#include "packets.h"
7e9dcc0f 29
135ee7ef
JH
30#if IFLA_INFO_MAX < 5
31#define IFLA_INFO_SLAVE_KIND 4
32#endif
33
7e9dcc0f
AW
34static struct nln *nln = NULL;
35static struct rtnetlink_change rtn_change;
36
37/* Returns true if the given netlink msg type corresponds to RTNLGRP_LINK. */
38bool
39rtnetlink_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. */
46bool
47rtnetlink_type_is_rtnlgrp_addr(uint16_t type)
48{
49 return type == RTM_NEWADDR || type == RTM_DELADDR;
50}
51
135ee7ef
JH
52/* Parses nested nlattr for link info. Returns false if unparseable, else
53 * populates 'change' and returns true. */
54static bool
55rtnetlink_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) {
91fc374a
BP
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);
135ee7ef
JH
77 }
78
79 return parsed;
80}
81
7e9dcc0f
AW
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. */
85bool
86rtnetlink_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 },
135ee7ef 101 [IFLA_LINKINFO] = { .type = NL_A_NESTED, .optional = true },
7e9dcc0f
AW
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] &&
74ff3298
JR
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);
7e9dcc0f 129 } else {
74ff3298 130 memset(&change->mac, 0, ETH_ADDR_LEN);
7e9dcc0f 131 }
135ee7ef
JH
132
133 if (attrs[IFLA_LINKINFO]) {
134 parsed = rtnetlink_parse_link_info(attrs[IFLA_LINKINFO],
135 change);
136 } else {
91fc374a
BP
137 change->primary = NULL;
138 change->sub = NULL;
135ee7ef 139 }
7e9dcc0f
AW
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[] = {
989d7135 147 [IFA_LABEL] = { .type = NL_A_STRING, .optional = true },
7e9dcc0f
AW
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;
989d7135
PS
162 change->ifname = (attrs[IFA_LABEL]
163 ? nl_attr_get_string(attrs[IFA_LABEL])
164 : NULL);
7e9dcc0f
AW
165 }
166 }
167
168 return parsed;
169}
170
77ee67e4
JR
171/* Return RTNLGRP_LINK on success, 0 on parse error. */
172static int
7e9dcc0f
AW
173rtnetlink_parse_cb(struct ofpbuf *buf, void *change)
174{
77ee67e4 175 return rtnetlink_parse(buf, change) ? RTNLGRP_LINK : 0;
7e9dcc0f
AW
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. */
189struct nln_notifier *
190rtnetlink_notifier_create(rtnetlink_notify_func *cb, void *aux)
191{
192 if (!nln) {
77ee67e4 193 nln = nln_create(NETLINK_ROUTE, rtnetlink_parse_cb, &rtn_change);
7e9dcc0f
AW
194 }
195
77ee67e4 196 return nln_notifier_create(nln, RTNLGRP_LINK, (nln_notify_func *) cb, aux);
7e9dcc0f
AW
197}
198
199/* Destroys 'notifier', which must have previously been created with
200 * rtnetlink_notifier_register(). */
201void
202rtnetlink_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. */
209void
210rtnetlink_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. */
219void
220rtnetlink_wait(void)
221{
222 if (nln) {
223 nln_wait(nln);
224 }
225}
e8e1a409
TZ
226
227/* Report RTNLGRP_LINK netdev change events. */
228void
229rtnetlink_report_link(void)
230{
231 if (nln) {
232 nln_report(nln, NULL, RTNLGRP_LINK);
233 }
234}