]>
Commit | Line | Data |
---|---|---|
21d6e22e | 1 | /* |
098d85b3 | 2 | * Copyright (c) 2009, 2010, 2013 Nicira, Inc. |
21d6e22e EJ |
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-link.h" | |
20 | ||
21 | #include <sys/socket.h> | |
22 | #include <linux/rtnetlink.h> | |
23 | #include <net/if.h> | |
24 | ||
25 | #include "netlink.h" | |
45c8d3a1 | 26 | #include "netlink-notifier.h" |
21d6e22e | 27 | #include "ofpbuf.h" |
21d6e22e | 28 | |
0a811051 | 29 | static struct nln *nln = NULL; |
21d6e22e EJ |
30 | static struct rtnetlink_link_change rtn_change; |
31 | ||
ea83a2fc EJ |
32 | /* Parses a rtnetlink message 'buf' into 'change'. If 'buf' is unparseable, |
33 | * leaves 'change' untouched and returns false. Otherwise, populates 'change' | |
34 | * and returns true. */ | |
35 | bool | |
21d6e22e EJ |
36 | rtnetlink_link_parse(struct ofpbuf *buf, |
37 | struct rtnetlink_link_change *change) | |
38 | { | |
39 | bool parsed; | |
40 | ||
41 | /* Policy for RTNLGRP_LINK messages. | |
42 | * | |
43 | * There are *many* more fields in these messages, but currently we | |
44 | * only care about these fields. */ | |
45 | static const struct nl_policy policy[] = { | |
46 | [IFLA_IFNAME] = { .type = NL_A_STRING, .optional = false }, | |
47 | [IFLA_MASTER] = { .type = NL_A_U32, .optional = true }, | |
90a6637d | 48 | [IFLA_MTU] = { .type = NL_A_U32, .optional = true }, |
44445cac | 49 | [IFLA_ADDRESS] = { .type = NL_A_UNSPEC, .optional = true }, |
21d6e22e EJ |
50 | }; |
51 | ||
098d85b3 | 52 | struct nlattr *attrs[ARRAY_SIZE(policy)]; |
21d6e22e EJ |
53 | |
54 | parsed = nl_policy_parse(buf, NLMSG_HDRLEN + sizeof(struct ifinfomsg), | |
55 | policy, attrs, ARRAY_SIZE(policy)); | |
56 | ||
57 | if (parsed) { | |
58 | const struct nlmsghdr *nlmsg; | |
59 | const struct ifinfomsg *ifinfo; | |
60 | ||
6fd6ed71 | 61 | nlmsg = buf->data; |
db5a1019 | 62 | ifinfo = ofpbuf_at(buf, NLMSG_HDRLEN, sizeof *ifinfo); |
21d6e22e EJ |
63 | |
64 | change->nlmsg_type = nlmsg->nlmsg_type; | |
65 | change->ifi_index = ifinfo->ifi_index; | |
66 | change->ifname = nl_attr_get_string(attrs[IFLA_IFNAME]); | |
c37d4da4 | 67 | change->ifi_flags = ifinfo->ifi_flags; |
21d6e22e EJ |
68 | change->master_ifindex = (attrs[IFLA_MASTER] |
69 | ? nl_attr_get_u32(attrs[IFLA_MASTER]) | |
70 | : 0); | |
90a6637d PS |
71 | change->mtu = (attrs[IFLA_MTU] |
72 | ? nl_attr_get_u32(attrs[IFLA_MTU]) | |
73 | : 0); | |
74 | ||
44445cac PS |
75 | if (attrs[IFLA_ADDRESS] && |
76 | nl_attr_get_size(attrs[IFLA_ADDRESS]) == ETH_ALEN) { | |
77 | memcpy(change->addr, nl_attr_get(attrs[IFLA_ADDRESS]), ETH_ALEN); | |
78 | } else { | |
79 | memset(change->addr, 0, ETH_ALEN); | |
80 | } | |
21d6e22e EJ |
81 | } |
82 | ||
83 | return parsed; | |
84 | } | |
85 | ||
9292989d BP |
86 | static bool |
87 | rtnetlink_link_parse_cb(struct ofpbuf *buf, void *change) | |
88 | { | |
89 | return rtnetlink_link_parse(buf, change); | |
90 | } | |
91 | ||
21d6e22e EJ |
92 | /* Registers 'cb' to be called with auxiliary data 'aux' with network device |
93 | * change notifications. The notifier is stored in 'notifier', which the | |
94 | * caller must not modify or free. | |
95 | * | |
96 | * This is probably not the function that you want. You should probably be | |
ac4d3bcb EJ |
97 | * using dpif_port_poll() or netdev_change_seq(), which unlike this function |
98 | * are not Linux-specific. | |
21d6e22e | 99 | * |
2ee6545f EJ |
100 | * Returns an initialized nln_notifier if successful, NULL otherwise. */ |
101 | struct nln_notifier * | |
102 | rtnetlink_link_notifier_create(rtnetlink_link_notify_func *cb, void *aux) | |
21d6e22e | 103 | { |
0a811051 EJ |
104 | if (!nln) { |
105 | nln = nln_create(NETLINK_ROUTE, RTNLGRP_LINK, rtnetlink_link_parse_cb, | |
106 | &rtn_change); | |
21d6e22e EJ |
107 | } |
108 | ||
2ee6545f | 109 | return nln_notifier_create(nln, (nln_notify_func *) cb, aux); |
21d6e22e EJ |
110 | } |
111 | ||
2ee6545f EJ |
112 | /* Destroys 'notifier', which must have previously been created with |
113 | * rtnetlink_link_notifier_register(). */ | |
21d6e22e | 114 | void |
2ee6545f | 115 | rtnetlink_link_notifier_destroy(struct nln_notifier *notifier) |
21d6e22e | 116 | { |
2ee6545f | 117 | nln_notifier_destroy(notifier); |
21d6e22e EJ |
118 | } |
119 | ||
120 | /* Calls all of the registered notifiers, passing along any as-yet-unreported | |
121 | * netdev change events. */ | |
122 | void | |
18a23781 | 123 | rtnetlink_link_run(void) |
21d6e22e | 124 | { |
0a811051 | 125 | if (nln) { |
18a23781 | 126 | nln_run(nln); |
21d6e22e EJ |
127 | } |
128 | } | |
129 | ||
130 | /* Causes poll_block() to wake up when network device change notifications are | |
131 | * ready. */ | |
132 | void | |
18a23781 | 133 | rtnetlink_link_wait(void) |
21d6e22e | 134 | { |
0a811051 | 135 | if (nln) { |
18a23781 | 136 | nln_wait(nln); |
21d6e22e EJ |
137 | } |
138 | } |