]>
Commit | Line | Data |
---|---|---|
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 |
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 | ||
135ee7ef JH |
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) { | |
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. */ | |
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 }, | |
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. */ |
172 | static int | |
7e9dcc0f AW |
173 | rtnetlink_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. */ | |
189 | struct nln_notifier * | |
190 | rtnetlink_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(). */ | |
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 | } | |
e8e1a409 TZ |
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 | } |