]>
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 AW |
29 | |
30 | static struct nln *nln = NULL; | |
31 | static struct rtnetlink_change rtn_change; | |
32 | ||
33 | /* Returns true if the given netlink msg type corresponds to RTNLGRP_LINK. */ | |
34 | bool | |
35 | rtnetlink_type_is_rtnlgrp_link(uint16_t type) | |
36 | { | |
37 | return type == RTM_NEWLINK || type == RTM_DELLINK; | |
38 | } | |
39 | ||
40 | /* Returns true if the given netlink msg type corresponds to | |
41 | * RTNLGRP_IPV4_IFADDR or RTNLGRP_IPV6_IFADDR. */ | |
42 | bool | |
43 | rtnetlink_type_is_rtnlgrp_addr(uint16_t type) | |
44 | { | |
45 | return type == RTM_NEWADDR || type == RTM_DELADDR; | |
46 | } | |
47 | ||
48 | /* Parses a rtnetlink message 'buf' into 'change'. If 'buf' is unparseable, | |
49 | * leaves 'change' untouched and returns false. Otherwise, populates 'change' | |
50 | * and returns true. */ | |
51 | bool | |
52 | rtnetlink_parse(struct ofpbuf *buf, struct rtnetlink_change *change) | |
53 | { | |
54 | const struct nlmsghdr *nlmsg = buf->data; | |
55 | bool parsed = false; | |
56 | ||
57 | if (rtnetlink_type_is_rtnlgrp_link(nlmsg->nlmsg_type)) { | |
58 | /* Policy for RTNLGRP_LINK messages. | |
59 | * | |
60 | * There are *many* more fields in these messages, but currently we | |
61 | * only care about these fields. */ | |
62 | static const struct nl_policy policy[] = { | |
63 | [IFLA_IFNAME] = { .type = NL_A_STRING, .optional = false }, | |
64 | [IFLA_MASTER] = { .type = NL_A_U32, .optional = true }, | |
65 | [IFLA_MTU] = { .type = NL_A_U32, .optional = true }, | |
66 | [IFLA_ADDRESS] = { .type = NL_A_UNSPEC, .optional = true }, | |
67 | }; | |
68 | ||
69 | struct nlattr *attrs[ARRAY_SIZE(policy)]; | |
70 | ||
71 | parsed = nl_policy_parse(buf, NLMSG_HDRLEN + sizeof(struct ifinfomsg), | |
72 | policy, attrs, ARRAY_SIZE(policy)); | |
73 | ||
74 | if (parsed) { | |
75 | const struct ifinfomsg *ifinfo; | |
76 | ||
77 | ifinfo = ofpbuf_at(buf, NLMSG_HDRLEN, sizeof *ifinfo); | |
78 | ||
79 | change->nlmsg_type = nlmsg->nlmsg_type; | |
80 | change->if_index = ifinfo->ifi_index; | |
81 | change->ifname = nl_attr_get_string(attrs[IFLA_IFNAME]); | |
82 | change->ifi_flags = ifinfo->ifi_flags; | |
83 | change->master_ifindex = (attrs[IFLA_MASTER] | |
84 | ? nl_attr_get_u32(attrs[IFLA_MASTER]) | |
85 | : 0); | |
86 | change->mtu = (attrs[IFLA_MTU] | |
87 | ? nl_attr_get_u32(attrs[IFLA_MTU]) | |
88 | : 0); | |
89 | ||
90 | if (attrs[IFLA_ADDRESS] && | |
74ff3298 JR |
91 | nl_attr_get_size(attrs[IFLA_ADDRESS]) == ETH_ADDR_LEN) { |
92 | memcpy(&change->mac, nl_attr_get(attrs[IFLA_ADDRESS]), | |
93 | ETH_ADDR_LEN); | |
7e9dcc0f | 94 | } else { |
74ff3298 | 95 | memset(&change->mac, 0, ETH_ADDR_LEN); |
7e9dcc0f AW |
96 | } |
97 | } | |
98 | } else if (rtnetlink_type_is_rtnlgrp_addr(nlmsg->nlmsg_type)) { | |
99 | /* Policy for RTNLGRP_IPV4_IFADDR/RTNLGRP_IPV6_IFADDR messages. | |
100 | * | |
101 | * There are *many* more fields in these messages, but currently we | |
102 | * only care about these fields. */ | |
103 | static const struct nl_policy policy[] = { | |
989d7135 | 104 | [IFA_LABEL] = { .type = NL_A_STRING, .optional = true }, |
7e9dcc0f AW |
105 | }; |
106 | ||
107 | struct nlattr *attrs[ARRAY_SIZE(policy)]; | |
108 | ||
109 | parsed = nl_policy_parse(buf, NLMSG_HDRLEN + sizeof(struct ifaddrmsg), | |
110 | policy, attrs, ARRAY_SIZE(policy)); | |
111 | ||
112 | if (parsed) { | |
113 | const struct ifaddrmsg *ifaddr; | |
114 | ||
115 | ifaddr = ofpbuf_at(buf, NLMSG_HDRLEN, sizeof *ifaddr); | |
116 | ||
117 | change->nlmsg_type = nlmsg->nlmsg_type; | |
118 | change->if_index = ifaddr->ifa_index; | |
989d7135 PS |
119 | change->ifname = (attrs[IFA_LABEL] |
120 | ? nl_attr_get_string(attrs[IFA_LABEL]) | |
121 | : NULL); | |
7e9dcc0f AW |
122 | } |
123 | } | |
124 | ||
125 | return parsed; | |
126 | } | |
127 | ||
77ee67e4 JR |
128 | /* Return RTNLGRP_LINK on success, 0 on parse error. */ |
129 | static int | |
7e9dcc0f AW |
130 | rtnetlink_parse_cb(struct ofpbuf *buf, void *change) |
131 | { | |
77ee67e4 | 132 | return rtnetlink_parse(buf, change) ? RTNLGRP_LINK : 0; |
7e9dcc0f AW |
133 | } |
134 | ||
135 | /* Registers 'cb' to be called with auxiliary data 'aux' with network device | |
136 | * change notifications. The notifier is stored in 'notifier', which the | |
137 | * caller must not modify or free. | |
138 | * | |
139 | * This is probably not the function that you want. You should probably be | |
140 | * using dpif_port_poll() or netdev_change_seq(), which unlike this function | |
141 | * are not Linux-specific. | |
142 | * | |
143 | * xxx Joins more multicast groups when needed. | |
144 | * | |
145 | * Returns an initialized nln_notifier if successful, NULL otherwise. */ | |
146 | struct nln_notifier * | |
147 | rtnetlink_notifier_create(rtnetlink_notify_func *cb, void *aux) | |
148 | { | |
149 | if (!nln) { | |
77ee67e4 | 150 | nln = nln_create(NETLINK_ROUTE, rtnetlink_parse_cb, &rtn_change); |
7e9dcc0f AW |
151 | } |
152 | ||
77ee67e4 | 153 | return nln_notifier_create(nln, RTNLGRP_LINK, (nln_notify_func *) cb, aux); |
7e9dcc0f AW |
154 | } |
155 | ||
156 | /* Destroys 'notifier', which must have previously been created with | |
157 | * rtnetlink_notifier_register(). */ | |
158 | void | |
159 | rtnetlink_notifier_destroy(struct nln_notifier *notifier) | |
160 | { | |
161 | nln_notifier_destroy(notifier); | |
162 | } | |
163 | ||
164 | /* Calls all of the registered notifiers, passing along any as-yet-unreported | |
165 | * netdev change events. */ | |
166 | void | |
167 | rtnetlink_run(void) | |
168 | { | |
169 | if (nln) { | |
170 | nln_run(nln); | |
171 | } | |
172 | } | |
173 | ||
174 | /* Causes poll_block() to wake up when network device change notifications are | |
175 | * ready. */ | |
176 | void | |
177 | rtnetlink_wait(void) | |
178 | { | |
179 | if (nln) { | |
180 | nln_wait(nln); | |
181 | } | |
182 | } |