]>
Commit | Line | Data |
---|---|---|
f6eb6b20 | 1 | /* |
10a89ef0 | 2 | * Copyright (c) 2011, 2013 Gaetano Catalli. |
f6eb6b20 GL |
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 <unistd.h> | |
20 | #include <errno.h> | |
21 | #include <sys/socket.h> | |
22 | #include <net/if.h> | |
23 | #include <net/route.h> | |
24 | #include <poll.h> | |
25 | ||
26 | #include "coverage.h" | |
27 | #include "socket-util.h" | |
28 | #include "poll-loop.h" | |
e6211adc | 29 | #include "openvswitch/vlog.h" |
f6eb6b20 GL |
30 | #include "rtbsd.h" |
31 | ||
32 | VLOG_DEFINE_THIS_MODULE(rtbsd); | |
33 | COVERAGE_DEFINE(rtbsd_changed); | |
34 | ||
a1f63b1e RW |
35 | static struct ovs_mutex rtbsd_mutex = OVS_MUTEX_INITIALIZER; |
36 | ||
f6eb6b20 GL |
37 | /* PF_ROUTE socket. */ |
38 | static int notify_sock = -1; | |
39 | ||
40 | /* All registered notifiers. */ | |
55951e15 | 41 | static struct ovs_list all_notifiers = OVS_LIST_INITIALIZER(&all_notifiers); |
f6eb6b20 | 42 | |
a1f63b1e RW |
43 | static void rtbsd_report_change(const struct if_msghdr *) |
44 | OVS_REQUIRES(rtbsd_mutex); | |
45 | static void rtbsd_report_notify_error(void) OVS_REQUIRES(rtbsd_mutex); | |
f6eb6b20 GL |
46 | |
47 | /* Registers 'cb' to be called with auxiliary data 'aux' with network device | |
48 | * change notifications. The notifier is stored in 'notifier', which the | |
49 | * caller must not modify or free. | |
50 | * | |
51 | * Returns 0 if successful, otherwise a positive errno value. */ | |
52 | int | |
53 | rtbsd_notifier_register(struct rtbsd_notifier *notifier, | |
54 | rtbsd_notify_func *cb, void *aux) | |
a1f63b1e | 55 | OVS_EXCLUDED(rtbsd_mutex) |
f6eb6b20 | 56 | { |
a1f63b1e RW |
57 | int error = 0; |
58 | ||
59 | ovs_mutex_lock(&rtbsd_mutex); | |
f6eb6b20 | 60 | if (notify_sock < 0) { |
f6eb6b20 GL |
61 | notify_sock = socket(PF_ROUTE, SOCK_RAW, 0); |
62 | if (notify_sock < 0) { | |
63 | VLOG_WARN("could not create PF_ROUTE socket: %s", | |
10a89ef0 | 64 | ovs_strerror(errno)); |
a1f63b1e RW |
65 | error = errno; |
66 | goto out; | |
f6eb6b20 GL |
67 | } |
68 | error = set_nonblocking(notify_sock); | |
69 | if (error) { | |
70 | VLOG_WARN("error set_nonblocking PF_ROUTE socket: %s", | |
10a89ef0 | 71 | ovs_strerror(error)); |
a1f63b1e | 72 | goto out; |
f6eb6b20 | 73 | } |
f6eb6b20 GL |
74 | } |
75 | ||
76 | list_push_back(&all_notifiers, ¬ifier->node); | |
77 | notifier->cb = cb; | |
78 | notifier->aux = aux; | |
a1f63b1e RW |
79 | |
80 | out: | |
81 | ovs_mutex_unlock(&rtbsd_mutex); | |
82 | return error; | |
f6eb6b20 GL |
83 | } |
84 | ||
85 | /* Cancels notification on 'notifier', which must have previously been | |
86 | * registered with rtbsd_notifier_register(). */ | |
87 | void | |
88 | rtbsd_notifier_unregister(struct rtbsd_notifier *notifier) | |
a1f63b1e | 89 | OVS_EXCLUDED(rtbsd_mutex) |
f6eb6b20 | 90 | { |
a1f63b1e | 91 | ovs_mutex_lock(&rtbsd_mutex); |
f6eb6b20 GL |
92 | list_remove(¬ifier->node); |
93 | if (list_is_empty(&all_notifiers)) { | |
94 | close(notify_sock); | |
95 | notify_sock = -1; | |
96 | } | |
a1f63b1e | 97 | ovs_mutex_unlock(&rtbsd_mutex); |
f6eb6b20 GL |
98 | } |
99 | ||
100 | /* Calls all of the registered notifiers, passing along any as-yet-unreported | |
101 | * netdev change events. */ | |
102 | void | |
103 | rtbsd_notifier_run(void) | |
a1f63b1e | 104 | OVS_EXCLUDED(rtbsd_mutex) |
f6eb6b20 GL |
105 | { |
106 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); | |
107 | struct if_msghdr msg; | |
a1f63b1e RW |
108 | |
109 | ovs_mutex_lock(&rtbsd_mutex); | |
f6eb6b20 | 110 | if (notify_sock < 0) { |
a1f63b1e | 111 | ovs_mutex_unlock(&rtbsd_mutex); |
f6eb6b20 GL |
112 | return; |
113 | } | |
114 | ||
115 | for (;;) { | |
116 | int retval; | |
117 | ||
118 | msg.ifm_type = RTM_IFINFO; | |
119 | msg.ifm_version = RTM_VERSION; //XXX check if necessary | |
120 | ||
121 | /* read from PF_ROUTE socket */ | |
122 | retval = read(notify_sock, (char *)&msg, sizeof(msg)); | |
123 | if (retval >= 0) { | |
124 | /* received packet from PF_ROUTE socket | |
125 | * XXX check for bad packets */ | |
126 | if (msg.ifm_type == RTM_IFINFO) { | |
127 | rtbsd_report_change(&msg); | |
128 | } | |
129 | } else if (errno == EAGAIN) { | |
a1f63b1e | 130 | ovs_mutex_unlock(&rtbsd_mutex); |
f6eb6b20 GL |
131 | return; |
132 | } else { | |
133 | if (errno == ENOBUFS) { | |
134 | VLOG_WARN_RL(&rl, "PF_ROUTE receive buffer overflowed"); | |
135 | } else { | |
136 | VLOG_WARN_RL(&rl, "error reading PF_ROUTE socket: %s", | |
10a89ef0 | 137 | ovs_strerror(errno)); |
f6eb6b20 GL |
138 | } |
139 | rtbsd_report_notify_error(); | |
140 | } | |
141 | } | |
142 | } | |
143 | ||
144 | /* Causes poll_block() to wake up when network device change notifications are | |
145 | * ready. */ | |
146 | void | |
147 | rtbsd_notifier_wait(void) | |
a1f63b1e | 148 | OVS_EXCLUDED(rtbsd_mutex) |
f6eb6b20 | 149 | { |
a1f63b1e | 150 | ovs_mutex_lock(&rtbsd_mutex); |
f6eb6b20 GL |
151 | if (notify_sock >= 0) { |
152 | poll_fd_wait(notify_sock, POLLIN); | |
153 | } | |
a1f63b1e | 154 | ovs_mutex_unlock(&rtbsd_mutex); |
f6eb6b20 GL |
155 | } |
156 | ||
157 | static void | |
158 | rtbsd_report_change(const struct if_msghdr *msg) | |
a1f63b1e | 159 | OVS_REQUIRES(rtbsd_mutex) |
f6eb6b20 GL |
160 | { |
161 | struct rtbsd_notifier *notifier; | |
162 | struct rtbsd_change change; | |
163 | ||
164 | COVERAGE_INC(rtbsd_changed); | |
165 | ||
166 | change.msg_type = msg->ifm_type; //XXX | |
167 | change.if_index = msg->ifm_index; | |
168 | if_indextoname(msg->ifm_index, change.if_name); | |
169 | change.master_ifindex = 0; //XXX | |
170 | ||
171 | LIST_FOR_EACH (notifier, node, &all_notifiers) { | |
172 | notifier->cb(&change, notifier->aux); | |
173 | } | |
174 | } | |
175 | ||
176 | /* If an error occurs the notifiers' callbacks are called with NULL changes */ | |
177 | static void | |
178 | rtbsd_report_notify_error(void) | |
a1f63b1e | 179 | OVS_REQUIRES(rtbsd_mutex) |
f6eb6b20 GL |
180 | { |
181 | struct rtbsd_notifier *notifier; | |
182 | ||
183 | LIST_FOR_EACH (notifier, node, &all_notifiers) { | |
184 | notifier->cb(NULL, notifier->aux); | |
185 | } | |
186 | } |