Coverage Report

Created: 2025-07-01 06:50

/src/openvswitch/lib/rtnetlink.c
Line
Count
Source (jump to first uncovered line)
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
0
#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
0
{
41
0
    return type == RTM_NEWLINK || type == RTM_DELLINK;
42
0
}
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
0
{
49
0
    return type == RTM_NEWADDR || type == RTM_DELADDR;
50
0
}
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
0
{
58
0
    bool parsed = false;
59
60
0
    static const struct nl_policy linkinfo_policy[] = {
61
0
        [IFLA_INFO_KIND] = { .type = NL_A_STRING, .optional = true  },
62
0
        [IFLA_INFO_SLAVE_KIND] = { .type = NL_A_STRING, .optional = true  },
63
0
    };
64
65
0
    struct nlattr *linkinfo[ARRAY_SIZE(linkinfo_policy)];
66
67
0
    parsed = nl_parse_nested(nla, linkinfo_policy, linkinfo,
68
0
                             ARRAY_SIZE(linkinfo_policy));
69
70
0
    if (parsed) {
71
0
        change->primary = (linkinfo[IFLA_INFO_KIND]
72
0
                           ? nl_attr_get_string(linkinfo[IFLA_INFO_KIND])
73
0
                           : NULL);
74
0
        change->sub = (linkinfo[IFLA_INFO_SLAVE_KIND]
75
0
                       ? nl_attr_get_string(linkinfo[IFLA_INFO_SLAVE_KIND])
76
0
                       : NULL);
77
0
    }
78
79
0
    return parsed;
80
0
}
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
0
{
88
0
    const struct nlmsghdr *nlmsg = buf->data;
89
0
    bool parsed = false;
90
91
0
    change->irrelevant = false;
92
93
0
    if (rtnetlink_type_is_rtnlgrp_link(nlmsg->nlmsg_type)) {
94
        /* Policy for RTNLGRP_LINK messages.
95
         *
96
         * There are *many* more fields in these messages, but currently we
97
         * only care about these fields. */
98
0
        static const struct nl_policy policy[] = {
99
0
            [IFLA_IFNAME] = { .type = NL_A_STRING, .optional = false },
100
0
            [IFLA_MASTER] = { .type = NL_A_U32,    .optional = true },
101
0
            [IFLA_MTU]    = { .type = NL_A_U32,    .optional = true },
102
0
            [IFLA_ADDRESS] = { .type = NL_A_UNSPEC, .optional = true },
103
0
            [IFLA_LINKINFO] = { .type = NL_A_NESTED, .optional = true },
104
0
            [IFLA_WIRELESS] = { .type = NL_A_UNSPEC, .optional = true },
105
0
        };
106
107
0
        struct nlattr *attrs[ARRAY_SIZE(policy)];
108
109
0
        parsed = nl_policy_parse(buf, NLMSG_HDRLEN + sizeof(struct ifinfomsg),
110
0
                                 policy, attrs, ARRAY_SIZE(policy));
111
112
0
        if (parsed) {
113
0
            const struct ifinfomsg *ifinfo;
114
115
0
            ifinfo = ofpbuf_at_assert(buf, NLMSG_HDRLEN, sizeof *ifinfo);
116
117
            /* Wireless events can be spammy and cause a
118
             * lot of unnecessary churn and CPU load in
119
             * ovs-vswitchd. The best way to filter them out
120
             * is to rely on the IFLA_WIRELESS and
121
             * ifi_change. As per rtnetlink_ifinfo_prep() in
122
             * the kernel, the ifi_change = 0. That combined
123
             * with the fact wireless events never really
124
             * change interface state (as far as core
125
             * networking is concerned) they can be ignored
126
             * by ovs-vswitchd. It doesn't understand
127
             * wireless extensions anyway and has no way of
128
             * presenting these bits into ovsdb.
129
             */
130
0
            if (attrs[IFLA_WIRELESS] && ifinfo->ifi_change == 0) {
131
0
                change->irrelevant = true;
132
0
            }
133
134
0
            change->nlmsg_type     = nlmsg->nlmsg_type;
135
0
            change->if_index       = ifinfo->ifi_index;
136
0
            change->ifname         = nl_attr_get_string(attrs[IFLA_IFNAME]);
137
0
            change->ifi_flags      = ifinfo->ifi_flags;
138
0
            change->master_ifindex = (attrs[IFLA_MASTER]
139
0
                                      ? nl_attr_get_u32(attrs[IFLA_MASTER])
140
0
                                      : 0);
141
0
            change->mtu            = (attrs[IFLA_MTU]
142
0
                                      ? nl_attr_get_u32(attrs[IFLA_MTU])
143
0
                                      : 0);
144
145
0
            if (attrs[IFLA_ADDRESS] &&
146
0
                nl_attr_get_size(attrs[IFLA_ADDRESS]) == ETH_ADDR_LEN) {
147
0
                memcpy(&change->mac, nl_attr_get(attrs[IFLA_ADDRESS]),
148
0
                       ETH_ADDR_LEN);
149
0
            } else {
150
0
                memset(&change->mac, 0, ETH_ADDR_LEN);
151
0
            }
152
153
0
            if (attrs[IFLA_LINKINFO]) {
154
0
                parsed = rtnetlink_parse_link_info(attrs[IFLA_LINKINFO],
155
0
                                                   change);
156
0
            } else {
157
0
                change->primary = NULL;
158
0
                change->sub = NULL;
159
0
            }
160
0
        }
161
0
    } else if (rtnetlink_type_is_rtnlgrp_addr(nlmsg->nlmsg_type)) {
162
        /* Policy for RTNLGRP_IPV4_IFADDR/RTNLGRP_IPV6_IFADDR messages.
163
         *
164
         * There are *many* more fields in these messages, but currently we
165
         * only care about these fields. */
166
0
        static const struct nl_policy policy[] = {
167
0
            [IFA_LABEL] = { .type = NL_A_STRING, .optional = true },
168
0
        };
169
170
0
        struct nlattr *attrs[ARRAY_SIZE(policy)];
171
172
0
        parsed = nl_policy_parse(buf, NLMSG_HDRLEN + sizeof(struct ifaddrmsg),
173
0
                                 policy, attrs, ARRAY_SIZE(policy));
174
175
0
        if (parsed) {
176
0
            const struct ifaddrmsg *ifaddr;
177
178
0
            ifaddr = ofpbuf_at_assert(buf, NLMSG_HDRLEN, sizeof *ifaddr);
179
180
0
            change->nlmsg_type     = nlmsg->nlmsg_type;
181
0
            change->if_index       = ifaddr->ifa_index;
182
0
            change->ifname         = (attrs[IFA_LABEL]
183
0
                                      ? nl_attr_get_string(attrs[IFA_LABEL])
184
0
                                      : NULL);
185
0
        }
186
0
    }
187
188
0
    return parsed;
189
0
}
190
191
/* Return RTNLGRP_LINK on success, 0 on parse error. */
192
static int
193
rtnetlink_parse_cb(struct ofpbuf *buf, void *change)
194
0
{
195
0
    return rtnetlink_parse(buf, change) ? RTNLGRP_LINK : 0;
196
0
}
197
198
/* Registers 'cb' to be called with auxiliary data 'aux' with network device
199
 * change notifications.  The notifier is stored in 'notifier', which the
200
 * caller must not modify or free.
201
 *
202
 * This is probably not the function that you want.  You should probably be
203
 * using dpif_port_poll() or netdev_change_seq(), which unlike this function
204
 * are not Linux-specific.
205
 *
206
 * xxx Joins more multicast groups when needed.
207
 *
208
 * Returns an initialized nln_notifier if successful, NULL otherwise. */
209
struct nln_notifier *
210
rtnetlink_notifier_create(rtnetlink_notify_func *cb, void *aux)
211
0
{
212
0
    if (!nln) {
213
0
        nln = nln_create(NETLINK_ROUTE, rtnetlink_parse_cb, &rtn_change);
214
0
    }
215
216
0
    return nln_notifier_create(nln, RTNLGRP_LINK, (nln_notify_func *) cb, aux);
217
0
}
218
219
/* Destroys 'notifier', which must have previously been created with
220
 * rtnetlink_notifier_register(). */
221
void
222
rtnetlink_notifier_destroy(struct nln_notifier *notifier)
223
0
{
224
0
    nln_notifier_destroy(notifier);
225
0
}
226
227
/* Calls all of the registered notifiers, passing along any as-yet-unreported
228
 * netdev change events. */
229
void
230
rtnetlink_run(void)
231
0
{
232
0
    if (nln) {
233
0
        nln_run(nln);
234
0
    }
235
0
}
236
237
/* Causes poll_block() to wake up when network device change notifications are
238
 * ready. */
239
void
240
rtnetlink_wait(void)
241
0
{
242
0
    if (nln) {
243
0
        nln_wait(nln);
244
0
    }
245
0
}
246
247
/* Report RTNLGRP_LINK netdev change events. */
248
void
249
rtnetlink_report_link(void)
250
0
{
251
0
    if (nln) {
252
0
        nln_report(nln, NULL, RTNLGRP_LINK);
253
0
    }
254
0
}