]>
Commit | Line | Data |
---|---|---|
96fba48f | 1 | /* |
aa5c0216 | 2 | * Copyright (c) 2008-2017 Nicira, Inc. |
96fba48f BP |
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> | |
9fe3b9a2 | 18 | |
93451a0a | 19 | #include "dpif-netlink.h" |
96fba48f | 20 | |
96fba48f BP |
21 | #include <ctype.h> |
22 | #include <errno.h> | |
23 | #include <fcntl.h> | |
24 | #include <inttypes.h> | |
25 | #include <net/if.h> | |
b90fa799 | 26 | #include <linux/types.h> |
aae51f53 | 27 | #include <linux/pkt_sched.h> |
8522ba09 | 28 | #include <poll.h> |
96fba48f | 29 | #include <stdlib.h> |
8522ba09 | 30 | #include <strings.h> |
50f80534 | 31 | #include <sys/epoll.h> |
10dcf8de | 32 | #include <sys/stat.h> |
96fba48f BP |
33 | #include <unistd.h> |
34 | ||
773cd538 | 35 | #include "bitmap.h" |
96fba48f | 36 | #include "dpif-provider.h" |
c4e08753 | 37 | #include "dpif-netlink-rtnl.h" |
3e8a2ad1 | 38 | #include "openvswitch/dynamic-string.h" |
eb8b28e7 | 39 | #include "flow.h" |
1579cf67 | 40 | #include "fat-rwlock.h" |
3abc4a1a | 41 | #include "netdev.h" |
032aa6a3 | 42 | #include "netdev-linux.h" |
c3827f61 | 43 | #include "netdev-vport.h" |
c11c9f4a | 44 | #include "netlink-conntrack.h" |
45c8d3a1 | 45 | #include "netlink-notifier.h" |
982b8810 | 46 | #include "netlink-socket.h" |
856081f6 | 47 | #include "netlink.h" |
feebdea2 | 48 | #include "odp-util.h" |
64c96779 | 49 | #include "openvswitch/ofpbuf.h" |
856081f6 | 50 | #include "packets.h" |
96fba48f | 51 | #include "poll-loop.h" |
17411ecf | 52 | #include "random.h" |
ee89ea7b | 53 | #include "openvswitch/shash.h" |
b3c01ed3 | 54 | #include "sset.h" |
14b4d2f9 | 55 | #include "timeval.h" |
d6569377 | 56 | #include "unaligned.h" |
96fba48f | 57 | #include "util.h" |
e6211adc | 58 | #include "openvswitch/vlog.h" |
2482b0b0 | 59 | #include "openvswitch/flow.h" |
5136ce49 | 60 | |
93451a0a | 61 | VLOG_DEFINE_THIS_MODULE(dpif_netlink); |
09cac43f | 62 | #ifdef _WIN32 |
da467899 | 63 | #include "wmi.h" |
09cac43f NR |
64 | enum { WINDOWS = 1 }; |
65 | #else | |
66 | enum { WINDOWS = 0 }; | |
67 | #endif | |
95b1d73a | 68 | enum { MAX_PORTS = USHRT_MAX }; |
773cd538 | 69 | |
24b019f8 JP |
70 | /* This ethtool flag was introduced in Linux 2.6.24, so it might be |
71 | * missing if we have old headers. */ | |
72 | #define ETH_FLAG_LRO (1 << 15) /* LRO is enabled */ | |
73 | ||
93451a0a | 74 | struct dpif_netlink_dp { |
aaff4b55 BP |
75 | /* Generic Netlink header. */ |
76 | uint8_t cmd; | |
d6569377 | 77 | |
df2c07f4 | 78 | /* struct ovs_header. */ |
254f2dc8 | 79 | int dp_ifindex; |
d6569377 BP |
80 | |
81 | /* Attributes. */ | |
df2c07f4 | 82 | const char *name; /* OVS_DP_ATTR_NAME. */ |
fcd5d230 | 83 | const uint32_t *upcall_pid; /* OVS_DP_ATTR_UPCALL_PID. */ |
b7fd5e38 | 84 | uint32_t user_features; /* OVS_DP_ATTR_USER_FEATURES */ |
6a54dedc BP |
85 | const struct ovs_dp_stats *stats; /* OVS_DP_ATTR_STATS. */ |
86 | const struct ovs_dp_megaflow_stats *megaflow_stats; | |
847108dc | 87 | /* OVS_DP_ATTR_MEGAFLOW_STATS.*/ |
d6569377 BP |
88 | }; |
89 | ||
93451a0a AS |
90 | static void dpif_netlink_dp_init(struct dpif_netlink_dp *); |
91 | static int dpif_netlink_dp_from_ofpbuf(struct dpif_netlink_dp *, | |
92 | const struct ofpbuf *); | |
93 | static void dpif_netlink_dp_dump_start(struct nl_dump *); | |
94 | static int dpif_netlink_dp_transact(const struct dpif_netlink_dp *request, | |
95 | struct dpif_netlink_dp *reply, | |
96 | struct ofpbuf **bufp); | |
97 | static int dpif_netlink_dp_get(const struct dpif *, | |
98 | struct dpif_netlink_dp *reply, | |
99 | struct ofpbuf **bufp); | |
100 | ||
101 | struct dpif_netlink_flow { | |
37a1300c BP |
102 | /* Generic Netlink header. */ |
103 | uint8_t cmd; | |
d6569377 | 104 | |
df2c07f4 | 105 | /* struct ovs_header. */ |
d6569377 | 106 | unsigned int nlmsg_flags; |
254f2dc8 | 107 | int dp_ifindex; |
d6569377 BP |
108 | |
109 | /* Attributes. | |
110 | * | |
0e70cdcb BP |
111 | * The 'stats' member points to 64-bit data that might only be aligned on |
112 | * 32-bit boundaries, so get_unaligned_u64() should be used to access its | |
113 | * values. | |
d2a23af2 | 114 | * |
df2c07f4 | 115 | * If 'actions' is nonnull then OVS_FLOW_ATTR_ACTIONS will be included in |
d2a23af2 | 116 | * the Netlink version of the command, even if actions_len is zero. */ |
df2c07f4 | 117 | const struct nlattr *key; /* OVS_FLOW_ATTR_KEY. */ |
d6569377 | 118 | size_t key_len; |
e6cc0bab AZ |
119 | const struct nlattr *mask; /* OVS_FLOW_ATTR_MASK. */ |
120 | size_t mask_len; | |
df2c07f4 | 121 | const struct nlattr *actions; /* OVS_FLOW_ATTR_ACTIONS. */ |
d6569377 | 122 | size_t actions_len; |
70e5ed6f JS |
123 | ovs_u128 ufid; /* OVS_FLOW_ATTR_FLOW_ID. */ |
124 | bool ufid_present; /* Is there a UFID? */ | |
125 | bool ufid_terse; /* Skip serializing key/mask/acts? */ | |
df2c07f4 JP |
126 | const struct ovs_flow_stats *stats; /* OVS_FLOW_ATTR_STATS. */ |
127 | const uint8_t *tcp_flags; /* OVS_FLOW_ATTR_TCP_FLAGS. */ | |
0e70cdcb | 128 | const ovs_32aligned_u64 *used; /* OVS_FLOW_ATTR_USED. */ |
df2c07f4 | 129 | bool clear; /* OVS_FLOW_ATTR_CLEAR. */ |
43f9ac0a | 130 | bool probe; /* OVS_FLOW_ATTR_PROBE. */ |
d6569377 BP |
131 | }; |
132 | ||
93451a0a AS |
133 | static void dpif_netlink_flow_init(struct dpif_netlink_flow *); |
134 | static int dpif_netlink_flow_from_ofpbuf(struct dpif_netlink_flow *, | |
135 | const struct ofpbuf *); | |
136 | static void dpif_netlink_flow_to_ofpbuf(const struct dpif_netlink_flow *, | |
137 | struct ofpbuf *); | |
138 | static int dpif_netlink_flow_transact(struct dpif_netlink_flow *request, | |
139 | struct dpif_netlink_flow *reply, | |
140 | struct ofpbuf **bufp); | |
141 | static void dpif_netlink_flow_get_stats(const struct dpif_netlink_flow *, | |
142 | struct dpif_flow_stats *); | |
7af12bd7 | 143 | static void dpif_netlink_flow_to_dpif_flow(struct dpif *, struct dpif_flow *, |
93451a0a | 144 | const struct dpif_netlink_flow *); |
d6569377 | 145 | |
989fd548 | 146 | /* One of the dpif channels between the kernel and userspace. */ |
fe3d61b3 | 147 | struct dpif_channel { |
14b4d2f9 | 148 | struct nl_sock *sock; /* Netlink socket. */ |
14b4d2f9 | 149 | long long int last_poll; /* Last time this channel was polled. */ |
fe3d61b3 BP |
150 | }; |
151 | ||
09cac43f NR |
152 | #ifdef _WIN32 |
153 | #define VPORT_SOCK_POOL_SIZE 1 | |
154 | /* On Windows, there is no native support for epoll. There are equivalent | |
155 | * interfaces though, that are not used currently. For simpicity, a pool of | |
156 | * netlink sockets is used. Each socket is represented by 'struct | |
157 | * dpif_windows_vport_sock'. Since it is a pool, multiple OVS ports may be | |
158 | * sharing the same socket. In the future, we can add a reference count and | |
159 | * such fields. */ | |
160 | struct dpif_windows_vport_sock { | |
161 | struct nl_sock *nl_sock; /* netlink socket. */ | |
162 | }; | |
163 | #endif | |
164 | ||
1579cf67 AW |
165 | struct dpif_handler { |
166 | struct dpif_channel *channels;/* Array of channels for each handler. */ | |
167 | struct epoll_event *epoll_events; | |
168 | int epoll_fd; /* epoll fd that includes channel socks. */ | |
169 | int n_events; /* Num events returned by epoll_wait(). */ | |
170 | int event_offset; /* Offset into 'epoll_events'. */ | |
09cac43f NR |
171 | |
172 | #ifdef _WIN32 | |
173 | /* Pool of sockets. */ | |
174 | struct dpif_windows_vport_sock *vport_sock_pool; | |
175 | size_t last_used_pool_idx; /* Index to aid in allocating a | |
176 | socket in the pool to a port. */ | |
177 | #endif | |
1579cf67 | 178 | }; |
14b4d2f9 | 179 | |
96fba48f | 180 | /* Datapath interface for the openvswitch Linux kernel module. */ |
93451a0a | 181 | struct dpif_netlink { |
96fba48f | 182 | struct dpif dpif; |
254f2dc8 | 183 | int dp_ifindex; |
e9e28be3 | 184 | |
b063d9f0 | 185 | /* Upcall messages. */ |
1579cf67 AW |
186 | struct fat_rwlock upcall_lock; |
187 | struct dpif_handler *handlers; | |
188 | uint32_t n_handlers; /* Num of upcall handlers. */ | |
189 | int uc_array_size; /* Size of 'handler->channels' and */ | |
190 | /* 'handler->epoll_events'. */ | |
982b8810 | 191 | |
e9e28be3 | 192 | /* Change notification. */ |
e4516b20 | 193 | struct nl_sock *port_notifier; /* vport multicast group subscriber. */ |
61eae437 | 194 | bool refresh_channels; |
96fba48f BP |
195 | }; |
196 | ||
93451a0a | 197 | static void report_loss(struct dpif_netlink *, struct dpif_channel *, |
9b00386b | 198 | uint32_t ch_idx, uint32_t handler_id); |
1579cf67 | 199 | |
96fba48f BP |
200 | static struct vlog_rate_limit error_rl = VLOG_RATE_LIMIT_INIT(9999, 5); |
201 | ||
e4516b20 BP |
202 | /* Generic Netlink family numbers for OVS. |
203 | * | |
93451a0a | 204 | * Initialized by dpif_netlink_init(). */ |
df2c07f4 JP |
205 | static int ovs_datapath_family; |
206 | static int ovs_vport_family; | |
207 | static int ovs_flow_family; | |
208 | static int ovs_packet_family; | |
982b8810 | 209 | |
e4516b20 BP |
210 | /* Generic Netlink multicast groups for OVS. |
211 | * | |
93451a0a | 212 | * Initialized by dpif_netlink_init(). */ |
e4516b20 | 213 | static unsigned int ovs_vport_mcgroup; |
982b8810 | 214 | |
921c370a EG |
215 | /* If true, tunnel devices are created using OVS compat/genetlink. |
216 | * If false, tunnel devices are created with rtnetlink and using light weight | |
217 | * tunnels. If we fail to create the tunnel the rtnetlink+LWT, then we fallback | |
218 | * to using the compat interface. */ | |
219 | static bool ovs_tunnels_out_of_tree = true; | |
220 | ||
93451a0a AS |
221 | static int dpif_netlink_init(void); |
222 | static int open_dpif(const struct dpif_netlink_dp *, struct dpif **); | |
223 | static uint32_t dpif_netlink_port_get_pid(const struct dpif *, | |
224 | odp_port_t port_no, uint32_t hash); | |
09cac43f | 225 | static void dpif_netlink_handler_uninit(struct dpif_handler *handler); |
93451a0a AS |
226 | static int dpif_netlink_refresh_channels(struct dpif_netlink *, |
227 | uint32_t n_handlers); | |
228 | static void dpif_netlink_vport_to_ofpbuf(const struct dpif_netlink_vport *, | |
229 | struct ofpbuf *); | |
230 | static int dpif_netlink_vport_from_ofpbuf(struct dpif_netlink_vport *, | |
231 | const struct ofpbuf *); | |
921c370a EG |
232 | static int dpif_netlink_port_query__(const struct dpif_netlink *dpif, |
233 | odp_port_t port_no, const char *port_name, | |
234 | struct dpif_port *dpif_port); | |
f0fef760 | 235 | |
93451a0a AS |
236 | static struct dpif_netlink * |
237 | dpif_netlink_cast(const struct dpif *dpif) | |
96fba48f | 238 | { |
93451a0a AS |
239 | dpif_assert_class(dpif, &dpif_netlink_class); |
240 | return CONTAINER_OF(dpif, struct dpif_netlink, dpif); | |
96fba48f BP |
241 | } |
242 | ||
d3d22744 | 243 | static int |
93451a0a AS |
244 | dpif_netlink_enumerate(struct sset *all_dps, |
245 | const struct dpif_class *dpif_class OVS_UNUSED) | |
d3d22744 | 246 | { |
aaff4b55 | 247 | struct nl_dump dump; |
d57695d7 JS |
248 | uint64_t reply_stub[NL_DUMP_BUFSIZE / 8]; |
249 | struct ofpbuf msg, buf; | |
aaff4b55 | 250 | int error; |
982b8810 | 251 | |
93451a0a | 252 | error = dpif_netlink_init(); |
aaff4b55 BP |
253 | if (error) { |
254 | return error; | |
982b8810 | 255 | } |
d3d22744 | 256 | |
d57695d7 | 257 | ofpbuf_use_stub(&buf, reply_stub, sizeof reply_stub); |
93451a0a | 258 | dpif_netlink_dp_dump_start(&dump); |
d57695d7 | 259 | while (nl_dump_next(&dump, &msg, &buf)) { |
93451a0a | 260 | struct dpif_netlink_dp dp; |
d6569377 | 261 | |
93451a0a | 262 | if (!dpif_netlink_dp_from_ofpbuf(&dp, &msg)) { |
d0c23a1a | 263 | sset_add(all_dps, dp.name); |
d3d22744 BP |
264 | } |
265 | } | |
d57695d7 | 266 | ofpbuf_uninit(&buf); |
aaff4b55 | 267 | return nl_dump_done(&dump); |
d3d22744 BP |
268 | } |
269 | ||
96fba48f | 270 | static int |
93451a0a AS |
271 | dpif_netlink_open(const struct dpif_class *class OVS_UNUSED, const char *name, |
272 | bool create, struct dpif **dpifp) | |
96fba48f | 273 | { |
93451a0a | 274 | struct dpif_netlink_dp dp_request, dp; |
c19e6535 | 275 | struct ofpbuf *buf; |
ea36840f | 276 | uint32_t upcall_pid; |
c19e6535 | 277 | int error; |
96fba48f | 278 | |
93451a0a | 279 | error = dpif_netlink_init(); |
982b8810 BP |
280 | if (error) { |
281 | return error; | |
282 | } | |
283 | ||
982b8810 | 284 | /* Create or look up datapath. */ |
93451a0a | 285 | dpif_netlink_dp_init(&dp_request); |
ea36840f BP |
286 | if (create) { |
287 | dp_request.cmd = OVS_DP_CMD_NEW; | |
288 | upcall_pid = 0; | |
289 | dp_request.upcall_pid = &upcall_pid; | |
290 | } else { | |
b7fd5e38 TG |
291 | /* Use OVS_DP_CMD_SET to report user features */ |
292 | dp_request.cmd = OVS_DP_CMD_SET; | |
ea36840f | 293 | } |
254f2dc8 | 294 | dp_request.name = name; |
b7fd5e38 | 295 | dp_request.user_features |= OVS_DP_F_UNALIGNED; |
1579cf67 | 296 | dp_request.user_features |= OVS_DP_F_VPORT_PIDS; |
93451a0a | 297 | error = dpif_netlink_dp_transact(&dp_request, &dp, &buf); |
982b8810 BP |
298 | if (error) { |
299 | return error; | |
c19e6535 | 300 | } |
254f2dc8 | 301 | |
e4516b20 | 302 | error = open_dpif(&dp, dpifp); |
8f4a4df5 | 303 | ofpbuf_delete(buf); |
e4516b20 | 304 | return error; |
c19e6535 BP |
305 | } |
306 | ||
e4516b20 | 307 | static int |
93451a0a | 308 | open_dpif(const struct dpif_netlink_dp *dp, struct dpif **dpifp) |
c19e6535 | 309 | { |
93451a0a | 310 | struct dpif_netlink *dpif; |
c19e6535 | 311 | |
17411ecf | 312 | dpif = xzalloc(sizeof *dpif); |
e4516b20 | 313 | dpif->port_notifier = NULL; |
1579cf67 | 314 | fat_rwlock_init(&dpif->upcall_lock); |
c19e6535 | 315 | |
93451a0a | 316 | dpif_init(&dpif->dpif, &dpif_netlink_class, dp->name, |
254f2dc8 | 317 | dp->dp_ifindex, dp->dp_ifindex); |
c19e6535 | 318 | |
254f2dc8 | 319 | dpif->dp_ifindex = dp->dp_ifindex; |
c19e6535 | 320 | *dpifp = &dpif->dpif; |
e4516b20 BP |
321 | |
322 | return 0; | |
96fba48f BP |
323 | } |
324 | ||
1579cf67 AW |
325 | /* Destroys the netlink sockets pointed by the elements in 'socksp' |
326 | * and frees the 'socksp'. */ | |
17411ecf | 327 | static void |
09cac43f | 328 | vport_del_socksp__(struct nl_sock **socksp, uint32_t n_socks) |
17411ecf | 329 | { |
1579cf67 | 330 | size_t i; |
17411ecf | 331 | |
1579cf67 AW |
332 | for (i = 0; i < n_socks; i++) { |
333 | nl_sock_destroy(socksp[i]); | |
50f80534 | 334 | } |
989fd548 | 335 | |
1579cf67 AW |
336 | free(socksp); |
337 | } | |
989fd548 | 338 | |
1579cf67 AW |
339 | /* Creates an array of netlink sockets. Returns an array of the |
340 | * corresponding pointers. Records the error in 'error'. */ | |
341 | static struct nl_sock ** | |
09cac43f | 342 | vport_create_socksp__(uint32_t n_socks, int *error) |
1579cf67 AW |
343 | { |
344 | struct nl_sock **socksp = xzalloc(n_socks * sizeof *socksp); | |
345 | size_t i; | |
346 | ||
347 | for (i = 0; i < n_socks; i++) { | |
348 | *error = nl_sock_create(NETLINK_GENERIC, &socksp[i]); | |
349 | if (*error) { | |
350 | goto error; | |
989fd548 | 351 | } |
1579cf67 | 352 | } |
989fd548 | 353 | |
1579cf67 | 354 | return socksp; |
9fafa796 | 355 | |
1579cf67 | 356 | error: |
09cac43f | 357 | vport_del_socksp__(socksp, n_socks); |
989fd548 | 358 | |
1579cf67 AW |
359 | return NULL; |
360 | } | |
361 | ||
09cac43f NR |
362 | #ifdef _WIN32 |
363 | static void | |
364 | vport_delete_sock_pool(struct dpif_handler *handler) | |
365 | OVS_REQ_WRLOCK(dpif->upcall_lock) | |
366 | { | |
367 | if (handler->vport_sock_pool) { | |
368 | uint32_t i; | |
369 | struct dpif_windows_vport_sock *sock_pool = | |
370 | handler->vport_sock_pool; | |
371 | ||
372 | for (i = 0; i < VPORT_SOCK_POOL_SIZE; i++) { | |
373 | if (sock_pool[i].nl_sock) { | |
374 | nl_sock_unsubscribe_packets(sock_pool[i].nl_sock); | |
375 | nl_sock_destroy(sock_pool[i].nl_sock); | |
376 | sock_pool[i].nl_sock = NULL; | |
377 | } | |
378 | } | |
379 | ||
380 | free(handler->vport_sock_pool); | |
381 | handler->vport_sock_pool = NULL; | |
382 | } | |
383 | } | |
384 | ||
385 | static int | |
386 | vport_create_sock_pool(struct dpif_handler *handler) | |
387 | OVS_REQ_WRLOCK(dpif->upcall_lock) | |
388 | { | |
389 | struct dpif_windows_vport_sock *sock_pool; | |
390 | size_t i; | |
391 | int error = 0; | |
392 | ||
393 | sock_pool = xzalloc(VPORT_SOCK_POOL_SIZE * sizeof *sock_pool); | |
394 | for (i = 0; i < VPORT_SOCK_POOL_SIZE; i++) { | |
395 | error = nl_sock_create(NETLINK_GENERIC, &sock_pool[i].nl_sock); | |
396 | if (error) { | |
397 | goto error; | |
398 | } | |
399 | ||
400 | /* Enable the netlink socket to receive packets. This is equivalent to | |
401 | * calling nl_sock_join_mcgroup() to receive events. */ | |
402 | error = nl_sock_subscribe_packets(sock_pool[i].nl_sock); | |
403 | if (error) { | |
404 | goto error; | |
405 | } | |
406 | } | |
407 | ||
408 | handler->vport_sock_pool = sock_pool; | |
409 | handler->last_used_pool_idx = 0; | |
410 | return 0; | |
411 | ||
412 | error: | |
413 | vport_delete_sock_pool(handler); | |
414 | return error; | |
415 | } | |
416 | ||
417 | /* Returns an array pointers to netlink sockets. The sockets are picked from a | |
418 | * pool. Records the error in 'error'. */ | |
419 | static struct nl_sock ** | |
420 | vport_create_socksp_windows(struct dpif_netlink *dpif, int *error) | |
421 | OVS_REQ_WRLOCK(dpif->upcall_lock) | |
422 | { | |
423 | uint32_t n_socks = dpif->n_handlers; | |
424 | struct nl_sock **socksp; | |
425 | size_t i; | |
426 | ||
427 | ovs_assert(n_socks <= 1); | |
428 | socksp = xzalloc(n_socks * sizeof *socksp); | |
429 | ||
430 | /* Pick netlink sockets to use in a round-robin fashion from each | |
431 | * handler's pool of sockets. */ | |
432 | for (i = 0; i < n_socks; i++) { | |
433 | struct dpif_handler *handler = &dpif->handlers[i]; | |
434 | struct dpif_windows_vport_sock *sock_pool = handler->vport_sock_pool; | |
435 | size_t index = handler->last_used_pool_idx; | |
436 | ||
437 | /* A pool of sockets is allocated when the handler is initialized. */ | |
438 | if (sock_pool == NULL) { | |
439 | free(socksp); | |
440 | *error = EINVAL; | |
441 | return NULL; | |
442 | } | |
443 | ||
444 | ovs_assert(index < VPORT_SOCK_POOL_SIZE); | |
445 | socksp[i] = sock_pool[index].nl_sock; | |
446 | socksp[i] = sock_pool[index].nl_sock; | |
447 | ovs_assert(socksp[i]); | |
448 | index = (index == VPORT_SOCK_POOL_SIZE - 1) ? 0 : index + 1; | |
449 | handler->last_used_pool_idx = index; | |
450 | } | |
451 | ||
452 | return socksp; | |
453 | } | |
454 | ||
455 | static void | |
456 | vport_del_socksp_windows(struct dpif_netlink *dpif, struct nl_sock **socksp) | |
457 | { | |
458 | free(socksp); | |
459 | } | |
460 | #endif /* _WIN32 */ | |
461 | ||
462 | static struct nl_sock ** | |
463 | vport_create_socksp(struct dpif_netlink *dpif, int *error) | |
464 | { | |
465 | #ifdef _WIN32 | |
466 | return vport_create_socksp_windows(dpif, error); | |
467 | #else | |
468 | return vport_create_socksp__(dpif->n_handlers, error); | |
469 | #endif | |
470 | } | |
471 | ||
472 | static void | |
473 | vport_del_socksp(struct dpif_netlink *dpif, struct nl_sock **socksp) | |
474 | { | |
475 | #ifdef _WIN32 | |
476 | vport_del_socksp_windows(dpif, socksp); | |
477 | #else | |
478 | vport_del_socksp__(socksp, dpif->n_handlers); | |
479 | #endif | |
480 | } | |
481 | ||
1579cf67 AW |
482 | /* Given the array of pointers to netlink sockets 'socksp', returns |
483 | * the array of corresponding pids. If the 'socksp' is NULL, returns | |
484 | * a single-element array of value 0. */ | |
485 | static uint32_t * | |
486 | vport_socksp_to_pids(struct nl_sock **socksp, uint32_t n_socks) | |
487 | { | |
488 | uint32_t *pids; | |
489 | ||
490 | if (!socksp) { | |
491 | pids = xzalloc(sizeof *pids); | |
492 | } else { | |
493 | size_t i; | |
494 | ||
495 | pids = xzalloc(n_socks * sizeof *pids); | |
496 | for (i = 0; i < n_socks; i++) { | |
497 | pids[i] = nl_sock_pid(socksp[i]); | |
498 | } | |
17411ecf | 499 | } |
989fd548 | 500 | |
1579cf67 AW |
501 | return pids; |
502 | } | |
503 | ||
504 | /* Given the port number 'port_idx', extracts the pids of netlink sockets | |
505 | * associated to the port and assigns it to 'upcall_pids'. */ | |
506 | static bool | |
93451a0a | 507 | vport_get_pids(struct dpif_netlink *dpif, uint32_t port_idx, |
1579cf67 AW |
508 | uint32_t **upcall_pids) |
509 | { | |
510 | uint32_t *pids; | |
511 | size_t i; | |
989fd548 | 512 | |
1579cf67 AW |
513 | /* Since the nl_sock can only be assigned in either all |
514 | * or none "dpif->handlers" channels, the following check | |
515 | * would suffice. */ | |
516 | if (!dpif->handlers[0].channels[port_idx].sock) { | |
517 | return false; | |
518 | } | |
09cac43f | 519 | ovs_assert(!WINDOWS || dpif->n_handlers <= 1); |
1579cf67 AW |
520 | |
521 | pids = xzalloc(dpif->n_handlers * sizeof *pids); | |
522 | ||
523 | for (i = 0; i < dpif->n_handlers; i++) { | |
524 | pids[i] = nl_sock_pid(dpif->handlers[i].channels[port_idx].sock); | |
525 | } | |
526 | ||
527 | *upcall_pids = pids; | |
989fd548 | 528 | |
1579cf67 | 529 | return true; |
989fd548 JP |
530 | } |
531 | ||
532 | static int | |
93451a0a | 533 | vport_add_channels(struct dpif_netlink *dpif, odp_port_t port_no, |
1579cf67 | 534 | struct nl_sock **socksp) |
989fd548 JP |
535 | { |
536 | struct epoll_event event; | |
4e022ec0 | 537 | uint32_t port_idx = odp_to_u32(port_no); |
1579cf67 AW |
538 | size_t i, j; |
539 | int error; | |
989fd548 | 540 | |
1579cf67 | 541 | if (dpif->handlers == NULL) { |
989fd548 JP |
542 | return 0; |
543 | } | |
544 | ||
1579cf67 AW |
545 | /* We assume that the datapath densely chooses port numbers, which can |
546 | * therefore be used as an index into 'channels' and 'epoll_events' of | |
547 | * 'dpif->handler'. */ | |
4e022ec0 AW |
548 | if (port_idx >= dpif->uc_array_size) { |
549 | uint32_t new_size = port_idx + 1; | |
989fd548 | 550 | |
12d76859 | 551 | if (new_size > MAX_PORTS) { |
989fd548 JP |
552 | VLOG_WARN_RL(&error_rl, "%s: datapath port %"PRIu32" too big", |
553 | dpif_name(&dpif->dpif), port_no); | |
554 | return EFBIG; | |
555 | } | |
556 | ||
1579cf67 AW |
557 | for (i = 0; i < dpif->n_handlers; i++) { |
558 | struct dpif_handler *handler = &dpif->handlers[i]; | |
559 | ||
560 | handler->channels = xrealloc(handler->channels, | |
561 | new_size * sizeof *handler->channels); | |
562 | ||
563 | for (j = dpif->uc_array_size; j < new_size; j++) { | |
564 | handler->channels[j].sock = NULL; | |
565 | } | |
566 | ||
567 | handler->epoll_events = xrealloc(handler->epoll_events, | |
568 | new_size * sizeof *handler->epoll_events); | |
989fd548 | 569 | |
1579cf67 | 570 | } |
989fd548 JP |
571 | dpif->uc_array_size = new_size; |
572 | } | |
573 | ||
574 | memset(&event, 0, sizeof event); | |
575 | event.events = EPOLLIN; | |
4e022ec0 | 576 | event.data.u32 = port_idx; |
989fd548 | 577 | |
1579cf67 AW |
578 | for (i = 0; i < dpif->n_handlers; i++) { |
579 | struct dpif_handler *handler = &dpif->handlers[i]; | |
580 | ||
09cac43f | 581 | #ifndef _WIN32 |
1579cf67 AW |
582 | if (epoll_ctl(handler->epoll_fd, EPOLL_CTL_ADD, nl_sock_fd(socksp[i]), |
583 | &event) < 0) { | |
584 | error = errno; | |
585 | goto error; | |
586 | } | |
93451a0a | 587 | #endif |
1579cf67 AW |
588 | dpif->handlers[i].channels[port_idx].sock = socksp[i]; |
589 | dpif->handlers[i].channels[port_idx].last_poll = LLONG_MIN; | |
590 | } | |
989fd548 JP |
591 | |
592 | return 0; | |
1579cf67 AW |
593 | |
594 | error: | |
595 | for (j = 0; j < i; j++) { | |
09cac43f | 596 | #ifndef _WIN32 |
1579cf67 AW |
597 | epoll_ctl(dpif->handlers[j].epoll_fd, EPOLL_CTL_DEL, |
598 | nl_sock_fd(socksp[j]), NULL); | |
93451a0a | 599 | #endif |
1579cf67 AW |
600 | dpif->handlers[j].channels[port_idx].sock = NULL; |
601 | } | |
602 | ||
603 | return error; | |
989fd548 JP |
604 | } |
605 | ||
606 | static void | |
93451a0a | 607 | vport_del_channels(struct dpif_netlink *dpif, odp_port_t port_no) |
989fd548 | 608 | { |
4e022ec0 | 609 | uint32_t port_idx = odp_to_u32(port_no); |
1579cf67 | 610 | size_t i; |
989fd548 | 611 | |
1579cf67 | 612 | if (!dpif->handlers || port_idx >= dpif->uc_array_size) { |
989fd548 JP |
613 | return; |
614 | } | |
615 | ||
1579cf67 AW |
616 | /* Since the sock can only be assigned in either all or none |
617 | * of "dpif->handlers" channels, the following check would | |
618 | * suffice. */ | |
619 | if (!dpif->handlers[0].channels[port_idx].sock) { | |
989fd548 JP |
620 | return; |
621 | } | |
622 | ||
1579cf67 AW |
623 | for (i = 0; i < dpif->n_handlers; i++) { |
624 | struct dpif_handler *handler = &dpif->handlers[i]; | |
09cac43f | 625 | #ifndef _WIN32 |
1579cf67 AW |
626 | epoll_ctl(handler->epoll_fd, EPOLL_CTL_DEL, |
627 | nl_sock_fd(handler->channels[port_idx].sock), NULL); | |
628 | nl_sock_destroy(handler->channels[port_idx].sock); | |
09cac43f | 629 | #endif |
1579cf67 AW |
630 | handler->channels[port_idx].sock = NULL; |
631 | handler->event_offset = handler->n_events = 0; | |
632 | } | |
633 | } | |
634 | ||
635 | static void | |
93451a0a AS |
636 | destroy_all_channels(struct dpif_netlink *dpif) |
637 | OVS_REQ_WRLOCK(dpif->upcall_lock) | |
1579cf67 AW |
638 | { |
639 | unsigned int i; | |
640 | ||
641 | if (!dpif->handlers) { | |
642 | return; | |
643 | } | |
644 | ||
645 | for (i = 0; i < dpif->uc_array_size; i++ ) { | |
93451a0a | 646 | struct dpif_netlink_vport vport_request; |
1579cf67 AW |
647 | uint32_t upcall_pids = 0; |
648 | ||
649 | /* Since the sock can only be assigned in either all or none | |
650 | * of "dpif->handlers" channels, the following check would | |
651 | * suffice. */ | |
652 | if (!dpif->handlers[0].channels[i].sock) { | |
653 | continue; | |
654 | } | |
655 | ||
656 | /* Turn off upcalls. */ | |
93451a0a | 657 | dpif_netlink_vport_init(&vport_request); |
1579cf67 AW |
658 | vport_request.cmd = OVS_VPORT_CMD_SET; |
659 | vport_request.dp_ifindex = dpif->dp_ifindex; | |
660 | vport_request.port_no = u32_to_odp(i); | |
a78f446a | 661 | vport_request.n_upcall_pids = 1; |
1579cf67 | 662 | vport_request.upcall_pids = &upcall_pids; |
93451a0a | 663 | dpif_netlink_vport_transact(&vport_request, NULL, NULL); |
1579cf67 AW |
664 | |
665 | vport_del_channels(dpif, u32_to_odp(i)); | |
666 | } | |
667 | ||
668 | for (i = 0; i < dpif->n_handlers; i++) { | |
669 | struct dpif_handler *handler = &dpif->handlers[i]; | |
670 | ||
09cac43f | 671 | dpif_netlink_handler_uninit(handler); |
1579cf67 AW |
672 | free(handler->epoll_events); |
673 | free(handler->channels); | |
674 | } | |
989fd548 | 675 | |
1579cf67 AW |
676 | free(dpif->handlers); |
677 | dpif->handlers = NULL; | |
678 | dpif->n_handlers = 0; | |
679 | dpif->uc_array_size = 0; | |
17411ecf JG |
680 | } |
681 | ||
96fba48f | 682 | static void |
93451a0a | 683 | dpif_netlink_close(struct dpif *dpif_) |
96fba48f | 684 | { |
93451a0a | 685 | struct dpif_netlink *dpif = dpif_netlink_cast(dpif_); |
c7178a0b | 686 | |
e4516b20 | 687 | nl_sock_destroy(dpif->port_notifier); |
1579cf67 AW |
688 | |
689 | fat_rwlock_wrlock(&dpif->upcall_lock); | |
690 | destroy_all_channels(dpif); | |
691 | fat_rwlock_unlock(&dpif->upcall_lock); | |
692 | ||
693 | fat_rwlock_destroy(&dpif->upcall_lock); | |
96fba48f BP |
694 | free(dpif); |
695 | } | |
696 | ||
697 | static int | |
93451a0a | 698 | dpif_netlink_destroy(struct dpif *dpif_) |
96fba48f | 699 | { |
93451a0a AS |
700 | struct dpif_netlink *dpif = dpif_netlink_cast(dpif_); |
701 | struct dpif_netlink_dp dp; | |
d6569377 | 702 | |
93451a0a | 703 | dpif_netlink_dp_init(&dp); |
df2c07f4 | 704 | dp.cmd = OVS_DP_CMD_DEL; |
254f2dc8 | 705 | dp.dp_ifindex = dpif->dp_ifindex; |
93451a0a | 706 | return dpif_netlink_dp_transact(&dp, NULL, NULL); |
96fba48f BP |
707 | } |
708 | ||
a36de779 | 709 | static bool |
93451a0a | 710 | dpif_netlink_run(struct dpif *dpif_) |
61eae437 | 711 | { |
93451a0a | 712 | struct dpif_netlink *dpif = dpif_netlink_cast(dpif_); |
1579cf67 | 713 | |
61eae437 BP |
714 | if (dpif->refresh_channels) { |
715 | dpif->refresh_channels = false; | |
1579cf67 | 716 | fat_rwlock_wrlock(&dpif->upcall_lock); |
93451a0a | 717 | dpif_netlink_refresh_channels(dpif, dpif->n_handlers); |
1579cf67 | 718 | fat_rwlock_unlock(&dpif->upcall_lock); |
61eae437 | 719 | } |
a36de779 | 720 | return false; |
61eae437 BP |
721 | } |
722 | ||
96fba48f | 723 | static int |
93451a0a | 724 | dpif_netlink_get_stats(const struct dpif *dpif_, struct dpif_dp_stats *stats) |
96fba48f | 725 | { |
93451a0a | 726 | struct dpif_netlink_dp dp; |
d6569377 BP |
727 | struct ofpbuf *buf; |
728 | int error; | |
729 | ||
93451a0a | 730 | error = dpif_netlink_dp_get(dpif_, &dp, &buf); |
d6569377 | 731 | if (!error) { |
6a54dedc BP |
732 | memset(stats, 0, sizeof *stats); |
733 | ||
734 | if (dp.stats) { | |
735 | stats->n_hit = get_32aligned_u64(&dp.stats->n_hit); | |
736 | stats->n_missed = get_32aligned_u64(&dp.stats->n_missed); | |
737 | stats->n_lost = get_32aligned_u64(&dp.stats->n_lost); | |
738 | stats->n_flows = get_32aligned_u64(&dp.stats->n_flows); | |
739 | } | |
740 | ||
741 | if (dp.megaflow_stats) { | |
742 | stats->n_masks = dp.megaflow_stats->n_masks; | |
743 | stats->n_mask_hit = get_32aligned_u64( | |
744 | &dp.megaflow_stats->n_mask_hit); | |
745 | } else { | |
746 | stats->n_masks = UINT32_MAX; | |
747 | stats->n_mask_hit = UINT64_MAX; | |
748 | } | |
d6569377 BP |
749 | ofpbuf_delete(buf); |
750 | } | |
751 | return error; | |
96fba48f BP |
752 | } |
753 | ||
b9ad7294 | 754 | static const char * |
93451a0a | 755 | get_vport_type(const struct dpif_netlink_vport *vport) |
b9ad7294 EJ |
756 | { |
757 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20); | |
758 | ||
759 | switch (vport->type) { | |
5ed51209 JS |
760 | case OVS_VPORT_TYPE_NETDEV: { |
761 | const char *type = netdev_get_type_from_name(vport->name); | |
762 | ||
763 | return type ? type : "system"; | |
764 | } | |
b9ad7294 EJ |
765 | |
766 | case OVS_VPORT_TYPE_INTERNAL: | |
767 | return "internal"; | |
768 | ||
c1fc1411 JG |
769 | case OVS_VPORT_TYPE_GENEVE: |
770 | return "geneve"; | |
771 | ||
b9ad7294 EJ |
772 | case OVS_VPORT_TYPE_GRE: |
773 | return "gre"; | |
774 | ||
b9ad7294 EJ |
775 | case OVS_VPORT_TYPE_VXLAN: |
776 | return "vxlan"; | |
777 | ||
a6ae068b LJ |
778 | case OVS_VPORT_TYPE_LISP: |
779 | return "lisp"; | |
780 | ||
4237026e PS |
781 | case OVS_VPORT_TYPE_STT: |
782 | return "stt"; | |
783 | ||
b9ad7294 EJ |
784 | case OVS_VPORT_TYPE_UNSPEC: |
785 | case __OVS_VPORT_TYPE_MAX: | |
786 | break; | |
787 | } | |
788 | ||
789 | VLOG_WARN_RL(&rl, "dp%d: port `%s' has unsupported type %u", | |
790 | vport->dp_ifindex, vport->name, (unsigned int) vport->type); | |
791 | return "unknown"; | |
792 | } | |
793 | ||
c4e08753 | 794 | enum ovs_vport_type |
20c57607 | 795 | netdev_to_ovs_vport_type(const char *type) |
c060c4cf | 796 | { |
c060c4cf EJ |
797 | if (!strcmp(type, "tap") || !strcmp(type, "system")) { |
798 | return OVS_VPORT_TYPE_NETDEV; | |
799 | } else if (!strcmp(type, "internal")) { | |
800 | return OVS_VPORT_TYPE_INTERNAL; | |
4237026e PS |
801 | } else if (strstr(type, "stt")) { |
802 | return OVS_VPORT_TYPE_STT; | |
c1fc1411 JG |
803 | } else if (!strcmp(type, "geneve")) { |
804 | return OVS_VPORT_TYPE_GENEVE; | |
c060c4cf EJ |
805 | } else if (strstr(type, "gre")) { |
806 | return OVS_VPORT_TYPE_GRE; | |
c060c4cf EJ |
807 | } else if (!strcmp(type, "vxlan")) { |
808 | return OVS_VPORT_TYPE_VXLAN; | |
a6ae068b LJ |
809 | } else if (!strcmp(type, "lisp")) { |
810 | return OVS_VPORT_TYPE_LISP; | |
c060c4cf EJ |
811 | } else { |
812 | return OVS_VPORT_TYPE_UNSPEC; | |
813 | } | |
814 | } | |
815 | ||
96fba48f | 816 | static int |
20c57607 EG |
817 | dpif_netlink_port_add__(struct dpif_netlink *dpif, const char *name, |
818 | enum ovs_vport_type type, | |
819 | struct ofpbuf *options, | |
93451a0a | 820 | odp_port_t *port_nop) |
b90de034 | 821 | OVS_REQ_WRLOCK(dpif->upcall_lock) |
96fba48f | 822 | { |
93451a0a | 823 | struct dpif_netlink_vport request, reply; |
c19e6535 | 824 | struct ofpbuf *buf; |
1579cf67 AW |
825 | struct nl_sock **socksp = NULL; |
826 | uint32_t *upcall_pids; | |
827 | int error = 0; | |
96fba48f | 828 | |
1579cf67 | 829 | if (dpif->handlers) { |
09cac43f | 830 | socksp = vport_create_socksp(dpif, &error); |
1579cf67 | 831 | if (!socksp) { |
989fd548 JP |
832 | return error; |
833 | } | |
834 | } | |
835 | ||
93451a0a | 836 | dpif_netlink_vport_init(&request); |
df2c07f4 | 837 | request.cmd = OVS_VPORT_CMD_NEW; |
254f2dc8 | 838 | request.dp_ifindex = dpif->dp_ifindex; |
20c57607 EG |
839 | request.type = type; |
840 | request.name = name; | |
841 | ||
842 | request.port_no = *port_nop; | |
843 | upcall_pids = vport_socksp_to_pids(socksp, dpif->n_handlers); | |
844 | request.n_upcall_pids = socksp ? dpif->n_handlers : 1; | |
845 | request.upcall_pids = upcall_pids; | |
846 | ||
847 | if (options) { | |
848 | request.options = options->data; | |
849 | request.options_len = options->size; | |
850 | } | |
851 | ||
852 | error = dpif_netlink_vport_transact(&request, &reply, &buf); | |
853 | if (!error) { | |
854 | *port_nop = reply.port_no; | |
855 | } else { | |
856 | if (error == EBUSY && *port_nop != ODPP_NONE) { | |
857 | VLOG_INFO("%s: requested port %"PRIu32" is in use", | |
858 | dpif_name(&dpif->dpif), *port_nop); | |
859 | } | |
860 | ||
861 | vport_del_socksp(dpif, socksp); | |
862 | goto exit; | |
863 | } | |
864 | ||
865 | if (socksp) { | |
866 | error = vport_add_channels(dpif, *port_nop, socksp); | |
867 | if (error) { | |
868 | VLOG_INFO("%s: could not add channel for port %s", | |
869 | dpif_name(&dpif->dpif), name); | |
870 | ||
871 | /* Delete the port. */ | |
872 | dpif_netlink_vport_init(&request); | |
873 | request.cmd = OVS_VPORT_CMD_DEL; | |
874 | request.dp_ifindex = dpif->dp_ifindex; | |
875 | request.port_no = *port_nop; | |
876 | dpif_netlink_vport_transact(&request, NULL, NULL); | |
877 | vport_del_socksp(dpif, socksp); | |
878 | goto exit; | |
879 | } | |
880 | } | |
881 | free(socksp); | |
882 | ||
883 | exit: | |
884 | ofpbuf_delete(buf); | |
885 | free(upcall_pids); | |
886 | ||
887 | return error; | |
888 | } | |
889 | ||
890 | static int | |
891 | dpif_netlink_port_add_compat(struct dpif_netlink *dpif, struct netdev *netdev, | |
892 | odp_port_t *port_nop) | |
893 | OVS_REQ_WRLOCK(dpif->upcall_lock) | |
894 | { | |
895 | const struct netdev_tunnel_config *tnl_cfg; | |
896 | char namebuf[NETDEV_VPORT_NAME_BUFSIZE]; | |
897 | const char *type = netdev_get_type(netdev); | |
898 | uint64_t options_stub[64 / 8]; | |
899 | enum ovs_vport_type ovs_type; | |
900 | struct ofpbuf options; | |
901 | const char *name; | |
902 | ||
903 | name = netdev_vport_get_dpif_port(netdev, namebuf, sizeof namebuf); | |
904 | ||
905 | ovs_type = netdev_to_ovs_vport_type(netdev_get_type(netdev)); | |
906 | if (ovs_type == OVS_VPORT_TYPE_UNSPEC) { | |
c283069c BP |
907 | VLOG_WARN_RL(&error_rl, "%s: cannot create port `%s' because it has " |
908 | "unsupported type `%s'", | |
9b00386b | 909 | dpif_name(&dpif->dpif), name, type); |
c283069c BP |
910 | return EINVAL; |
911 | } | |
c3827f61 | 912 | |
20c57607 | 913 | if (ovs_type == OVS_VPORT_TYPE_NETDEV) { |
93451a0a | 914 | #ifdef _WIN32 |
09cac43f | 915 | /* XXX : Map appropiate Windows handle */ |
93451a0a | 916 | #else |
24b019f8 | 917 | netdev_linux_ethtool_set_flag(netdev, ETH_FLAG_LRO, "LRO", false); |
93451a0a | 918 | #endif |
24b019f8 JP |
919 | } |
920 | ||
da467899 | 921 | #ifdef _WIN32 |
20c57607 | 922 | if (ovs_type == OVS_VPORT_TYPE_INTERNAL) { |
da467899 AS |
923 | if (!create_wmi_port(name)){ |
924 | VLOG_ERR("Could not create wmi internal port with name:%s", name); | |
da467899 AS |
925 | return EINVAL; |
926 | }; | |
927 | } | |
928 | #endif | |
929 | ||
26508d9a | 930 | tnl_cfg = netdev_get_tunnel_config(netdev); |
526df7d8 | 931 | if (tnl_cfg && (tnl_cfg->dst_port != 0 || tnl_cfg->exts)) { |
26508d9a | 932 | ofpbuf_use_stack(&options, options_stub, sizeof options_stub); |
526df7d8 TG |
933 | if (tnl_cfg->dst_port) { |
934 | nl_msg_put_u16(&options, OVS_TUNNEL_ATTR_DST_PORT, | |
935 | ntohs(tnl_cfg->dst_port)); | |
936 | } | |
937 | if (tnl_cfg->exts) { | |
938 | size_t ext_ofs; | |
939 | int i; | |
940 | ||
941 | ext_ofs = nl_msg_start_nested(&options, OVS_TUNNEL_ATTR_EXTENSION); | |
942 | for (i = 0; i < 32; i++) { | |
943 | if (tnl_cfg->exts & (1 << i)) { | |
944 | nl_msg_put_flag(&options, i); | |
945 | } | |
946 | } | |
947 | nl_msg_end_nested(&options, ext_ofs); | |
948 | } | |
20c57607 EG |
949 | return dpif_netlink_port_add__(dpif, name, ovs_type, &options, |
950 | port_nop); | |
2510ba7c | 951 | } else { |
20c57607 | 952 | return dpif_netlink_port_add__(dpif, name, ovs_type, NULL, port_nop); |
78a2d59c | 953 | } |
c3827f61 | 954 | |
20c57607 | 955 | } |
989fd548 | 956 | |
921c370a | 957 | static int |
c4e08753 EG |
958 | dpif_netlink_rtnl_port_create_and_add(struct dpif_netlink *dpif, |
959 | struct netdev *netdev, | |
960 | odp_port_t *port_nop) | |
961 | OVS_REQ_WRLOCK(dpif->upcall_lock) | |
962 | { | |
963 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20); | |
964 | char namebuf[NETDEV_VPORT_NAME_BUFSIZE]; | |
965 | const char *name; | |
966 | int error; | |
989fd548 | 967 | |
c4e08753 EG |
968 | error = dpif_netlink_rtnl_port_create(netdev); |
969 | if (error) { | |
970 | if (error != EOPNOTSUPP) { | |
971 | VLOG_INFO_RL(&rl, "Failed to create %s with rtnetlink: %s", | |
972 | netdev_get_name(netdev), ovs_strerror(error)); | |
973 | } | |
974 | return error; | |
975 | } | |
1579cf67 | 976 | |
c4e08753 EG |
977 | name = netdev_vport_get_dpif_port(netdev, namebuf, sizeof namebuf); |
978 | error = dpif_netlink_port_add__(dpif, name, OVS_VPORT_TYPE_NETDEV, NULL, | |
979 | port_nop); | |
980 | if (error) { | |
981 | dpif_netlink_rtnl_port_destroy(name, netdev_get_type(netdev)); | |
982 | } | |
983 | return error; | |
984 | } | |
96fba48f BP |
985 | |
986 | static int | |
93451a0a AS |
987 | dpif_netlink_port_add(struct dpif *dpif_, struct netdev *netdev, |
988 | odp_port_t *port_nop) | |
9fafa796 | 989 | { |
93451a0a | 990 | struct dpif_netlink *dpif = dpif_netlink_cast(dpif_); |
921c370a | 991 | int error = EOPNOTSUPP; |
9fafa796 | 992 | |
1579cf67 | 993 | fat_rwlock_wrlock(&dpif->upcall_lock); |
921c370a EG |
994 | if (!ovs_tunnels_out_of_tree) { |
995 | error = dpif_netlink_rtnl_port_create_and_add(dpif, netdev, port_nop); | |
996 | } | |
997 | if (error) { | |
998 | error = dpif_netlink_port_add_compat(dpif, netdev, port_nop); | |
999 | } | |
1579cf67 | 1000 | fat_rwlock_unlock(&dpif->upcall_lock); |
9fafa796 BP |
1001 | |
1002 | return error; | |
1003 | } | |
1004 | ||
1005 | static int | |
93451a0a | 1006 | dpif_netlink_port_del__(struct dpif_netlink *dpif, odp_port_t port_no) |
b90de034 | 1007 | OVS_REQ_WRLOCK(dpif->upcall_lock) |
96fba48f | 1008 | { |
93451a0a | 1009 | struct dpif_netlink_vport vport; |
921c370a | 1010 | struct dpif_port dpif_port; |
773cd538 | 1011 | int error; |
c19e6535 | 1012 | |
921c370a EG |
1013 | error = dpif_netlink_port_query__(dpif, port_no, NULL, &dpif_port); |
1014 | if (error) { | |
1015 | return error; | |
1016 | } | |
1017 | ||
93451a0a | 1018 | dpif_netlink_vport_init(&vport); |
df2c07f4 | 1019 | vport.cmd = OVS_VPORT_CMD_DEL; |
254f2dc8 | 1020 | vport.dp_ifindex = dpif->dp_ifindex; |
c19e6535 | 1021 | vport.port_no = port_no; |
da467899 | 1022 | #ifdef _WIN32 |
921c370a EG |
1023 | if (!strcmp(dpif_port.type, "internal")) { |
1024 | if (!delete_wmi_port(dpif_port.name)) { | |
da467899 | 1025 | VLOG_ERR("Could not delete wmi port with name: %s", |
921c370a | 1026 | dpif_port.name); |
da467899 AS |
1027 | }; |
1028 | } | |
1029 | #endif | |
93451a0a | 1030 | error = dpif_netlink_vport_transact(&vport, NULL, NULL); |
773cd538 | 1031 | |
1579cf67 | 1032 | vport_del_channels(dpif, port_no); |
989fd548 | 1033 | |
921c370a EG |
1034 | if (!error && !ovs_tunnels_out_of_tree) { |
1035 | error = dpif_netlink_rtnl_port_destroy(dpif_port.name, dpif_port.type); | |
1036 | if (error == EOPNOTSUPP) { | |
1037 | error = 0; | |
1038 | } | |
1039 | } | |
1040 | ||
1041 | dpif_port_destroy(&dpif_port); | |
1042 | ||
773cd538 | 1043 | return error; |
c3827f61 | 1044 | } |
3abc4a1a | 1045 | |
9fafa796 | 1046 | static int |
93451a0a | 1047 | dpif_netlink_port_del(struct dpif *dpif_, odp_port_t port_no) |
9fafa796 | 1048 | { |
93451a0a | 1049 | struct dpif_netlink *dpif = dpif_netlink_cast(dpif_); |
9fafa796 BP |
1050 | int error; |
1051 | ||
1579cf67 | 1052 | fat_rwlock_wrlock(&dpif->upcall_lock); |
93451a0a | 1053 | error = dpif_netlink_port_del__(dpif, port_no); |
1579cf67 | 1054 | fat_rwlock_unlock(&dpif->upcall_lock); |
9fafa796 BP |
1055 | |
1056 | return error; | |
1057 | } | |
1058 | ||
c3827f61 | 1059 | static int |
93451a0a AS |
1060 | dpif_netlink_port_query__(const struct dpif_netlink *dpif, odp_port_t port_no, |
1061 | const char *port_name, struct dpif_port *dpif_port) | |
c3827f61 | 1062 | { |
93451a0a AS |
1063 | struct dpif_netlink_vport request; |
1064 | struct dpif_netlink_vport reply; | |
c19e6535 | 1065 | struct ofpbuf *buf; |
4c738a8d BP |
1066 | int error; |
1067 | ||
93451a0a | 1068 | dpif_netlink_vport_init(&request); |
df2c07f4 | 1069 | request.cmd = OVS_VPORT_CMD_GET; |
9b00386b | 1070 | request.dp_ifindex = dpif->dp_ifindex; |
c19e6535 BP |
1071 | request.port_no = port_no; |
1072 | request.name = port_name; | |
4c738a8d | 1073 | |
93451a0a | 1074 | error = dpif_netlink_vport_transact(&request, &reply, &buf); |
c19e6535 | 1075 | if (!error) { |
33db1592 BP |
1076 | if (reply.dp_ifindex != request.dp_ifindex) { |
1077 | /* A query by name reported that 'port_name' is in some datapath | |
1078 | * other than 'dpif', but the caller wants to know about 'dpif'. */ | |
1079 | error = ENODEV; | |
4afba28d | 1080 | } else if (dpif_port) { |
33db1592 | 1081 | dpif_port->name = xstrdup(reply.name); |
b9ad7294 | 1082 | dpif_port->type = xstrdup(get_vport_type(&reply)); |
33db1592 BP |
1083 | dpif_port->port_no = reply.port_no; |
1084 | } | |
c19e6535 | 1085 | ofpbuf_delete(buf); |
3abc4a1a | 1086 | } |
c19e6535 | 1087 | return error; |
96fba48f BP |
1088 | } |
1089 | ||
1090 | static int | |
93451a0a AS |
1091 | dpif_netlink_port_query_by_number(const struct dpif *dpif_, odp_port_t port_no, |
1092 | struct dpif_port *dpif_port) | |
96fba48f | 1093 | { |
93451a0a | 1094 | struct dpif_netlink *dpif = dpif_netlink_cast(dpif_); |
9b00386b | 1095 | |
93451a0a | 1096 | return dpif_netlink_port_query__(dpif, port_no, NULL, dpif_port); |
96fba48f BP |
1097 | } |
1098 | ||
1099 | static int | |
93451a0a | 1100 | dpif_netlink_port_query_by_name(const struct dpif *dpif_, const char *devname, |
4c738a8d | 1101 | struct dpif_port *dpif_port) |
96fba48f | 1102 | { |
93451a0a | 1103 | struct dpif_netlink *dpif = dpif_netlink_cast(dpif_); |
9b00386b | 1104 | |
93451a0a | 1105 | return dpif_netlink_port_query__(dpif, 0, devname, dpif_port); |
96fba48f BP |
1106 | } |
1107 | ||
98403001 | 1108 | static uint32_t |
93451a0a AS |
1109 | dpif_netlink_port_get_pid__(const struct dpif_netlink *dpif, |
1110 | odp_port_t port_no, uint32_t hash) | |
b90de034 | 1111 | OVS_REQ_RDLOCK(dpif->upcall_lock) |
98403001 | 1112 | { |
4e022ec0 | 1113 | uint32_t port_idx = odp_to_u32(port_no); |
9fafa796 | 1114 | uint32_t pid = 0; |
98403001 | 1115 | |
f8fc5489 | 1116 | if (dpif->handlers && dpif->uc_array_size > 0) { |
4e022ec0 | 1117 | /* The ODPP_NONE "reserved" port number uses the "ovs-system"'s |
989fd548 | 1118 | * channel, since it is not heavily loaded. */ |
4e022ec0 | 1119 | uint32_t idx = port_idx >= dpif->uc_array_size ? 0 : port_idx; |
1579cf67 AW |
1120 | struct dpif_handler *h = &dpif->handlers[hash % dpif->n_handlers]; |
1121 | ||
17f2748d AW |
1122 | /* Needs to check in case the socket pointer is changed in between |
1123 | * the holding of upcall_lock. A known case happens when the main | |
1124 | * thread deletes the vport while the handler thread is handling | |
1125 | * the upcall from that port. */ | |
1126 | if (h->channels[idx].sock) { | |
1127 | pid = nl_sock_pid(h->channels[idx].sock); | |
1128 | } | |
98403001 | 1129 | } |
9fafa796 BP |
1130 | |
1131 | return pid; | |
98403001 BP |
1132 | } |
1133 | ||
b90de034 | 1134 | static uint32_t |
93451a0a AS |
1135 | dpif_netlink_port_get_pid(const struct dpif *dpif_, odp_port_t port_no, |
1136 | uint32_t hash) | |
b90de034 | 1137 | { |
93451a0a | 1138 | const struct dpif_netlink *dpif = dpif_netlink_cast(dpif_); |
b90de034 AW |
1139 | uint32_t ret; |
1140 | ||
1141 | fat_rwlock_rdlock(&dpif->upcall_lock); | |
93451a0a | 1142 | ret = dpif_netlink_port_get_pid__(dpif, port_no, hash); |
b90de034 AW |
1143 | fat_rwlock_unlock(&dpif->upcall_lock); |
1144 | ||
1145 | return ret; | |
1146 | } | |
1147 | ||
96fba48f | 1148 | static int |
93451a0a | 1149 | dpif_netlink_flow_flush(struct dpif *dpif_) |
96fba48f | 1150 | { |
93451a0a AS |
1151 | const struct dpif_netlink *dpif = dpif_netlink_cast(dpif_); |
1152 | struct dpif_netlink_flow flow; | |
37a1300c | 1153 | |
93451a0a | 1154 | dpif_netlink_flow_init(&flow); |
df2c07f4 | 1155 | flow.cmd = OVS_FLOW_CMD_DEL; |
254f2dc8 | 1156 | flow.dp_ifindex = dpif->dp_ifindex; |
93451a0a | 1157 | return dpif_netlink_flow_transact(&flow, NULL, NULL); |
96fba48f BP |
1158 | } |
1159 | ||
93451a0a | 1160 | struct dpif_netlink_port_state { |
f0fef760 | 1161 | struct nl_dump dump; |
d57695d7 | 1162 | struct ofpbuf buf; |
c19e6535 BP |
1163 | }; |
1164 | ||
222837c4 | 1165 | static void |
93451a0a AS |
1166 | dpif_netlink_port_dump_start__(const struct dpif_netlink *dpif, |
1167 | struct nl_dump *dump) | |
96fba48f | 1168 | { |
93451a0a | 1169 | struct dpif_netlink_vport request; |
f0fef760 BP |
1170 | struct ofpbuf *buf; |
1171 | ||
93451a0a | 1172 | dpif_netlink_vport_init(&request); |
067f1e23 | 1173 | request.cmd = OVS_VPORT_CMD_GET; |
254f2dc8 | 1174 | request.dp_ifindex = dpif->dp_ifindex; |
f0fef760 BP |
1175 | |
1176 | buf = ofpbuf_new(1024); | |
93451a0a | 1177 | dpif_netlink_vport_to_ofpbuf(&request, buf); |
222837c4 | 1178 | nl_dump_start(dump, NETLINK_GENERIC, buf); |
f0fef760 | 1179 | ofpbuf_delete(buf); |
222837c4 BP |
1180 | } |
1181 | ||
1182 | static int | |
93451a0a | 1183 | dpif_netlink_port_dump_start(const struct dpif *dpif_, void **statep) |
222837c4 | 1184 | { |
93451a0a AS |
1185 | struct dpif_netlink *dpif = dpif_netlink_cast(dpif_); |
1186 | struct dpif_netlink_port_state *state; | |
222837c4 BP |
1187 | |
1188 | *statep = state = xmalloc(sizeof *state); | |
93451a0a | 1189 | dpif_netlink_port_dump_start__(dpif, &state->dump); |
f0fef760 | 1190 | |
d57695d7 | 1191 | ofpbuf_init(&state->buf, NL_DUMP_BUFSIZE); |
b0ec0f27 BP |
1192 | return 0; |
1193 | } | |
1194 | ||
7c1ef244 | 1195 | static int |
93451a0a AS |
1196 | dpif_netlink_port_dump_next__(const struct dpif_netlink *dpif, |
1197 | struct nl_dump *dump, | |
1198 | struct dpif_netlink_vport *vport, | |
1199 | struct ofpbuf *buffer) | |
222837c4 | 1200 | { |
222837c4 BP |
1201 | struct ofpbuf buf; |
1202 | int error; | |
1203 | ||
d57695d7 | 1204 | if (!nl_dump_next(dump, &buf, buffer)) { |
222837c4 BP |
1205 | return EOF; |
1206 | } | |
1207 | ||
93451a0a | 1208 | error = dpif_netlink_vport_from_ofpbuf(vport, &buf); |
222837c4 BP |
1209 | if (error) { |
1210 | VLOG_WARN_RL(&error_rl, "%s: failed to parse vport record (%s)", | |
1211 | dpif_name(&dpif->dpif), ovs_strerror(error)); | |
1212 | } | |
1213 | return error; | |
1214 | } | |
1215 | ||
b0ec0f27 | 1216 | static int |
93451a0a AS |
1217 | dpif_netlink_port_dump_next(const struct dpif *dpif_, void *state_, |
1218 | struct dpif_port *dpif_port) | |
b0ec0f27 | 1219 | { |
93451a0a AS |
1220 | struct dpif_netlink *dpif = dpif_netlink_cast(dpif_); |
1221 | struct dpif_netlink_port_state *state = state_; | |
1222 | struct dpif_netlink_vport vport; | |
96fba48f BP |
1223 | int error; |
1224 | ||
93451a0a AS |
1225 | error = dpif_netlink_port_dump_next__(dpif, &state->dump, &vport, |
1226 | &state->buf); | |
c3827f61 | 1227 | if (error) { |
f0fef760 | 1228 | return error; |
c3827f61 | 1229 | } |
ebc56baa | 1230 | dpif_port->name = CONST_CAST(char *, vport.name); |
b9ad7294 | 1231 | dpif_port->type = CONST_CAST(char *, get_vport_type(&vport)); |
f0fef760 BP |
1232 | dpif_port->port_no = vport.port_no; |
1233 | return 0; | |
b0ec0f27 BP |
1234 | } |
1235 | ||
1236 | static int | |
93451a0a | 1237 | dpif_netlink_port_dump_done(const struct dpif *dpif_ OVS_UNUSED, void *state_) |
b0ec0f27 | 1238 | { |
93451a0a | 1239 | struct dpif_netlink_port_state *state = state_; |
f0fef760 | 1240 | int error = nl_dump_done(&state->dump); |
8522b383 | 1241 | |
d57695d7 | 1242 | ofpbuf_uninit(&state->buf); |
b0ec0f27 | 1243 | free(state); |
f0fef760 | 1244 | return error; |
96fba48f BP |
1245 | } |
1246 | ||
e9e28be3 | 1247 | static int |
93451a0a | 1248 | dpif_netlink_port_poll(const struct dpif *dpif_, char **devnamep) |
e9e28be3 | 1249 | { |
93451a0a | 1250 | struct dpif_netlink *dpif = dpif_netlink_cast(dpif_); |
e9e28be3 | 1251 | |
e4516b20 BP |
1252 | /* Lazily create the Netlink socket to listen for notifications. */ |
1253 | if (!dpif->port_notifier) { | |
1254 | struct nl_sock *sock; | |
1255 | int error; | |
1256 | ||
1257 | error = nl_sock_create(NETLINK_GENERIC, &sock); | |
1258 | if (error) { | |
1259 | return error; | |
1260 | } | |
1261 | ||
1262 | error = nl_sock_join_mcgroup(sock, ovs_vport_mcgroup); | |
1263 | if (error) { | |
1264 | nl_sock_destroy(sock); | |
1265 | return error; | |
1266 | } | |
1267 | dpif->port_notifier = sock; | |
1268 | ||
1269 | /* We have no idea of the current state so report that everything | |
1270 | * changed. */ | |
1271 | return ENOBUFS; | |
1272 | } | |
1273 | ||
1274 | for (;;) { | |
1275 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); | |
1276 | uint64_t buf_stub[4096 / 8]; | |
1277 | struct ofpbuf buf; | |
1278 | int error; | |
1279 | ||
1280 | ofpbuf_use_stub(&buf, buf_stub, sizeof buf_stub); | |
1281 | error = nl_sock_recv(dpif->port_notifier, &buf, false); | |
1282 | if (!error) { | |
93451a0a | 1283 | struct dpif_netlink_vport vport; |
e4516b20 | 1284 | |
93451a0a | 1285 | error = dpif_netlink_vport_from_ofpbuf(&vport, &buf); |
e4516b20 BP |
1286 | if (!error) { |
1287 | if (vport.dp_ifindex == dpif->dp_ifindex | |
1288 | && (vport.cmd == OVS_VPORT_CMD_NEW | |
1289 | || vport.cmd == OVS_VPORT_CMD_DEL | |
1290 | || vport.cmd == OVS_VPORT_CMD_SET)) { | |
1291 | VLOG_DBG("port_changed: dpif:%s vport:%s cmd:%"PRIu8, | |
1292 | dpif->dpif.full_name, vport.name, vport.cmd); | |
1579cf67 | 1293 | if (vport.cmd == OVS_VPORT_CMD_DEL && dpif->handlers) { |
61eae437 BP |
1294 | dpif->refresh_channels = true; |
1295 | } | |
e4516b20 | 1296 | *devnamep = xstrdup(vport.name); |
59e0c910 | 1297 | ofpbuf_uninit(&buf); |
e4516b20 | 1298 | return 0; |
e4516b20 BP |
1299 | } |
1300 | } | |
59e0c910 BP |
1301 | } else if (error != EAGAIN) { |
1302 | VLOG_WARN_RL(&rl, "error reading or parsing netlink (%s)", | |
1303 | ovs_strerror(error)); | |
1304 | nl_sock_drain(dpif->port_notifier); | |
1305 | error = ENOBUFS; | |
e4516b20 BP |
1306 | } |
1307 | ||
59e0c910 BP |
1308 | ofpbuf_uninit(&buf); |
1309 | if (error) { | |
1310 | return error; | |
1311 | } | |
e9e28be3 | 1312 | } |
e9e28be3 BP |
1313 | } |
1314 | ||
1315 | static void | |
93451a0a | 1316 | dpif_netlink_port_poll_wait(const struct dpif *dpif_) |
e9e28be3 | 1317 | { |
93451a0a | 1318 | const struct dpif_netlink *dpif = dpif_netlink_cast(dpif_); |
e4516b20 BP |
1319 | |
1320 | if (dpif->port_notifier) { | |
1321 | nl_sock_wait(dpif->port_notifier, POLLIN); | |
1322 | } else { | |
e9e28be3 | 1323 | poll_immediate_wake(); |
e9e28be3 BP |
1324 | } |
1325 | } | |
1326 | ||
6fe09f8c | 1327 | static void |
70e5ed6f JS |
1328 | dpif_netlink_flow_init_ufid(struct dpif_netlink_flow *request, |
1329 | const ovs_u128 *ufid, bool terse) | |
1330 | { | |
1331 | if (ufid) { | |
1332 | request->ufid = *ufid; | |
1333 | request->ufid_present = true; | |
1334 | } else { | |
1335 | request->ufid_present = false; | |
1336 | } | |
1337 | request->ufid_terse = terse; | |
1338 | } | |
1339 | ||
1340 | static void | |
1341 | dpif_netlink_init_flow_get__(const struct dpif_netlink *dpif, | |
1342 | const struct nlattr *key, size_t key_len, | |
1343 | const ovs_u128 *ufid, bool terse, | |
1344 | struct dpif_netlink_flow *request) | |
96fba48f | 1345 | { |
93451a0a | 1346 | dpif_netlink_flow_init(request); |
6fe09f8c JS |
1347 | request->cmd = OVS_FLOW_CMD_GET; |
1348 | request->dp_ifindex = dpif->dp_ifindex; | |
1349 | request->key = key; | |
1350 | request->key_len = key_len; | |
70e5ed6f JS |
1351 | dpif_netlink_flow_init_ufid(request, ufid, terse); |
1352 | } | |
1353 | ||
1354 | static void | |
1355 | dpif_netlink_init_flow_get(const struct dpif_netlink *dpif, | |
1356 | const struct dpif_flow_get *get, | |
1357 | struct dpif_netlink_flow *request) | |
1358 | { | |
1359 | dpif_netlink_init_flow_get__(dpif, get->key, get->key_len, get->ufid, | |
1360 | false, request); | |
30053024 BP |
1361 | } |
1362 | ||
1363 | static int | |
70e5ed6f JS |
1364 | dpif_netlink_flow_get__(const struct dpif_netlink *dpif, |
1365 | const struct nlattr *key, size_t key_len, | |
1366 | const ovs_u128 *ufid, bool terse, | |
1367 | struct dpif_netlink_flow *reply, struct ofpbuf **bufp) | |
30053024 | 1368 | { |
93451a0a | 1369 | struct dpif_netlink_flow request; |
30053024 | 1370 | |
70e5ed6f | 1371 | dpif_netlink_init_flow_get__(dpif, key, key_len, ufid, terse, &request); |
93451a0a | 1372 | return dpif_netlink_flow_transact(&request, reply, bufp); |
96fba48f BP |
1373 | } |
1374 | ||
70e5ed6f JS |
1375 | static int |
1376 | dpif_netlink_flow_get(const struct dpif_netlink *dpif, | |
1377 | const struct dpif_netlink_flow *flow, | |
1378 | struct dpif_netlink_flow *reply, struct ofpbuf **bufp) | |
1379 | { | |
1380 | return dpif_netlink_flow_get__(dpif, flow->key, flow->key_len, | |
1381 | flow->ufid_present ? &flow->ufid : NULL, | |
1382 | false, reply, bufp); | |
1383 | } | |
1384 | ||
6bc60024 | 1385 | static void |
93451a0a AS |
1386 | dpif_netlink_init_flow_put(struct dpif_netlink *dpif, |
1387 | const struct dpif_flow_put *put, | |
1388 | struct dpif_netlink_flow *request) | |
6bc60024 | 1389 | { |
d64e176c | 1390 | static const struct nlattr dummy_action; |
6bc60024 | 1391 | |
93451a0a | 1392 | dpif_netlink_flow_init(request); |
89625d1e | 1393 | request->cmd = (put->flags & DPIF_FP_CREATE |
6bc60024 BP |
1394 | ? OVS_FLOW_CMD_NEW : OVS_FLOW_CMD_SET); |
1395 | request->dp_ifindex = dpif->dp_ifindex; | |
89625d1e BP |
1396 | request->key = put->key; |
1397 | request->key_len = put->key_len; | |
e6cc0bab AZ |
1398 | request->mask = put->mask; |
1399 | request->mask_len = put->mask_len; | |
70e5ed6f JS |
1400 | dpif_netlink_flow_init_ufid(request, put->ufid, false); |
1401 | ||
6bc60024 | 1402 | /* Ensure that OVS_FLOW_ATTR_ACTIONS will always be included. */ |
d64e176c BP |
1403 | request->actions = (put->actions |
1404 | ? put->actions | |
1405 | : CONST_CAST(struct nlattr *, &dummy_action)); | |
89625d1e BP |
1406 | request->actions_len = put->actions_len; |
1407 | if (put->flags & DPIF_FP_ZERO_STATS) { | |
6bc60024 BP |
1408 | request->clear = true; |
1409 | } | |
43f9ac0a JR |
1410 | if (put->flags & DPIF_FP_PROBE) { |
1411 | request->probe = true; | |
1412 | } | |
89625d1e | 1413 | request->nlmsg_flags = put->flags & DPIF_FP_MODIFY ? 0 : NLM_F_CREATE; |
6bc60024 BP |
1414 | } |
1415 | ||
b99d3cee | 1416 | static void |
70e5ed6f JS |
1417 | dpif_netlink_init_flow_del__(struct dpif_netlink *dpif, |
1418 | const struct nlattr *key, size_t key_len, | |
1419 | const ovs_u128 *ufid, bool terse, | |
1420 | struct dpif_netlink_flow *request) | |
96fba48f | 1421 | { |
93451a0a | 1422 | dpif_netlink_flow_init(request); |
b99d3cee BP |
1423 | request->cmd = OVS_FLOW_CMD_DEL; |
1424 | request->dp_ifindex = dpif->dp_ifindex; | |
70e5ed6f JS |
1425 | request->key = key; |
1426 | request->key_len = key_len; | |
1427 | dpif_netlink_flow_init_ufid(request, ufid, terse); | |
1428 | } | |
1429 | ||
1430 | static void | |
1431 | dpif_netlink_init_flow_del(struct dpif_netlink *dpif, | |
1432 | const struct dpif_flow_del *del, | |
1433 | struct dpif_netlink_flow *request) | |
1434 | { | |
37382aa6 AS |
1435 | dpif_netlink_init_flow_del__(dpif, del->key, del->key_len, |
1436 | del->ufid, del->terse, request); | |
70e5ed6f JS |
1437 | } |
1438 | ||
93451a0a | 1439 | struct dpif_netlink_flow_dump { |
ac64794a BP |
1440 | struct dpif_flow_dump up; |
1441 | struct nl_dump nl_dump; | |
d2ad7ef1 | 1442 | atomic_int status; |
e723fd32 JS |
1443 | }; |
1444 | ||
93451a0a AS |
1445 | static struct dpif_netlink_flow_dump * |
1446 | dpif_netlink_flow_dump_cast(struct dpif_flow_dump *dump) | |
e723fd32 | 1447 | { |
93451a0a | 1448 | return CONTAINER_OF(dump, struct dpif_netlink_flow_dump, up); |
e723fd32 JS |
1449 | } |
1450 | ||
ac64794a | 1451 | static struct dpif_flow_dump * |
64bb477f | 1452 | dpif_netlink_flow_dump_create(const struct dpif *dpif_, bool terse) |
96fba48f | 1453 | { |
93451a0a AS |
1454 | const struct dpif_netlink *dpif = dpif_netlink_cast(dpif_); |
1455 | struct dpif_netlink_flow_dump *dump; | |
1456 | struct dpif_netlink_flow request; | |
37a1300c BP |
1457 | struct ofpbuf *buf; |
1458 | ||
ac64794a BP |
1459 | dump = xmalloc(sizeof *dump); |
1460 | dpif_flow_dump_init(&dump->up, dpif_); | |
37a1300c | 1461 | |
93451a0a | 1462 | dpif_netlink_flow_init(&request); |
067f1e23 | 1463 | request.cmd = OVS_FLOW_CMD_GET; |
254f2dc8 | 1464 | request.dp_ifindex = dpif->dp_ifindex; |
64bb477f JS |
1465 | request.ufid_present = false; |
1466 | request.ufid_terse = terse; | |
37a1300c BP |
1467 | |
1468 | buf = ofpbuf_new(1024); | |
93451a0a | 1469 | dpif_netlink_flow_to_ofpbuf(&request, buf); |
ac64794a | 1470 | nl_dump_start(&dump->nl_dump, NETLINK_GENERIC, buf); |
37a1300c | 1471 | ofpbuf_delete(buf); |
ac64794a | 1472 | atomic_init(&dump->status, 0); |
64bb477f | 1473 | dump->up.terse = terse; |
30053024 | 1474 | |
ac64794a | 1475 | return &dump->up; |
704a1e09 BP |
1476 | } |
1477 | ||
1478 | static int | |
93451a0a | 1479 | dpif_netlink_flow_dump_destroy(struct dpif_flow_dump *dump_) |
704a1e09 | 1480 | { |
93451a0a | 1481 | struct dpif_netlink_flow_dump *dump = dpif_netlink_flow_dump_cast(dump_); |
ac64794a BP |
1482 | unsigned int nl_status = nl_dump_done(&dump->nl_dump); |
1483 | int dump_status; | |
96fba48f | 1484 | |
7424fc44 JR |
1485 | /* No other thread has access to 'dump' at this point. */ |
1486 | atomic_read_relaxed(&dump->status, &dump_status); | |
ac64794a BP |
1487 | free(dump); |
1488 | return dump_status ? dump_status : nl_status; | |
1489 | } | |
feebdea2 | 1490 | |
93451a0a | 1491 | struct dpif_netlink_flow_dump_thread { |
ac64794a | 1492 | struct dpif_flow_dump_thread up; |
93451a0a AS |
1493 | struct dpif_netlink_flow_dump *dump; |
1494 | struct dpif_netlink_flow flow; | |
ac64794a BP |
1495 | struct dpif_flow_stats stats; |
1496 | struct ofpbuf nl_flows; /* Always used to store flows. */ | |
1497 | struct ofpbuf *nl_actions; /* Used if kernel does not supply actions. */ | |
1498 | }; | |
1499 | ||
93451a0a AS |
1500 | static struct dpif_netlink_flow_dump_thread * |
1501 | dpif_netlink_flow_dump_thread_cast(struct dpif_flow_dump_thread *thread) | |
ac64794a | 1502 | { |
93451a0a | 1503 | return CONTAINER_OF(thread, struct dpif_netlink_flow_dump_thread, up); |
ac64794a BP |
1504 | } |
1505 | ||
1506 | static struct dpif_flow_dump_thread * | |
93451a0a | 1507 | dpif_netlink_flow_dump_thread_create(struct dpif_flow_dump *dump_) |
ac64794a | 1508 | { |
93451a0a AS |
1509 | struct dpif_netlink_flow_dump *dump = dpif_netlink_flow_dump_cast(dump_); |
1510 | struct dpif_netlink_flow_dump_thread *thread; | |
ac64794a BP |
1511 | |
1512 | thread = xmalloc(sizeof *thread); | |
1513 | dpif_flow_dump_thread_init(&thread->up, &dump->up); | |
1514 | thread->dump = dump; | |
1515 | ofpbuf_init(&thread->nl_flows, NL_DUMP_BUFSIZE); | |
1516 | thread->nl_actions = NULL; | |
1517 | ||
1518 | return &thread->up; | |
1519 | } | |
1520 | ||
1521 | static void | |
93451a0a | 1522 | dpif_netlink_flow_dump_thread_destroy(struct dpif_flow_dump_thread *thread_) |
ac64794a | 1523 | { |
93451a0a AS |
1524 | struct dpif_netlink_flow_dump_thread *thread |
1525 | = dpif_netlink_flow_dump_thread_cast(thread_); | |
ac64794a BP |
1526 | |
1527 | ofpbuf_uninit(&thread->nl_flows); | |
1528 | ofpbuf_delete(thread->nl_actions); | |
1529 | free(thread); | |
1530 | } | |
1531 | ||
1532 | static void | |
7af12bd7 | 1533 | dpif_netlink_flow_to_dpif_flow(struct dpif *dpif, struct dpif_flow *dpif_flow, |
7fe98598 | 1534 | const struct dpif_netlink_flow *datapath_flow) |
ac64794a | 1535 | { |
7fe98598 NR |
1536 | dpif_flow->key = datapath_flow->key; |
1537 | dpif_flow->key_len = datapath_flow->key_len; | |
1538 | dpif_flow->mask = datapath_flow->mask; | |
1539 | dpif_flow->mask_len = datapath_flow->mask_len; | |
1540 | dpif_flow->actions = datapath_flow->actions; | |
1541 | dpif_flow->actions_len = datapath_flow->actions_len; | |
70e5ed6f | 1542 | dpif_flow->ufid_present = datapath_flow->ufid_present; |
ec97c2df | 1543 | dpif_flow->pmd_id = PMD_ID_NULL; |
70e5ed6f JS |
1544 | if (datapath_flow->ufid_present) { |
1545 | dpif_flow->ufid = datapath_flow->ufid; | |
1546 | } else { | |
1547 | ovs_assert(datapath_flow->key && datapath_flow->key_len); | |
1548 | dpif_flow_hash(dpif, datapath_flow->key, datapath_flow->key_len, | |
1549 | &dpif_flow->ufid); | |
1550 | } | |
7fe98598 | 1551 | dpif_netlink_flow_get_stats(datapath_flow, &dpif_flow->stats); |
ac64794a BP |
1552 | } |
1553 | ||
1554 | static int | |
93451a0a AS |
1555 | dpif_netlink_flow_dump_next(struct dpif_flow_dump_thread *thread_, |
1556 | struct dpif_flow *flows, int max_flows) | |
ac64794a | 1557 | { |
93451a0a AS |
1558 | struct dpif_netlink_flow_dump_thread *thread |
1559 | = dpif_netlink_flow_dump_thread_cast(thread_); | |
1560 | struct dpif_netlink_flow_dump *dump = thread->dump; | |
1561 | struct dpif_netlink *dpif = dpif_netlink_cast(thread->up.dpif); | |
ac64794a BP |
1562 | int n_flows; |
1563 | ||
1564 | ofpbuf_delete(thread->nl_actions); | |
1565 | thread->nl_actions = NULL; | |
1566 | ||
1567 | n_flows = 0; | |
1568 | while (!n_flows | |
6fd6ed71 | 1569 | || (n_flows < max_flows && thread->nl_flows.size)) { |
7fe98598 | 1570 | struct dpif_netlink_flow datapath_flow; |
ac64794a BP |
1571 | struct ofpbuf nl_flow; |
1572 | int error; | |
1573 | ||
1574 | /* Try to grab another flow. */ | |
1575 | if (!nl_dump_next(&dump->nl_dump, &nl_flow, &thread->nl_flows)) { | |
1576 | break; | |
feebdea2 | 1577 | } |
30053024 | 1578 | |
ac64794a | 1579 | /* Convert the flow to our output format. */ |
7fe98598 | 1580 | error = dpif_netlink_flow_from_ofpbuf(&datapath_flow, &nl_flow); |
30053024 | 1581 | if (error) { |
7424fc44 | 1582 | atomic_store_relaxed(&dump->status, error); |
ac64794a | 1583 | break; |
feebdea2 | 1584 | } |
30053024 | 1585 | |
64bb477f JS |
1586 | if (dump->up.terse || datapath_flow.actions) { |
1587 | /* Common case: we don't want actions, or the flow includes | |
1588 | * actions. */ | |
7af12bd7 JS |
1589 | dpif_netlink_flow_to_dpif_flow(&dpif->dpif, &flows[n_flows++], |
1590 | &datapath_flow); | |
ac64794a BP |
1591 | } else { |
1592 | /* Rare case: the flow does not include actions. Retrieve this | |
1593 | * individual flow again to get the actions. */ | |
70e5ed6f | 1594 | error = dpif_netlink_flow_get(dpif, &datapath_flow, |
7fe98598 | 1595 | &datapath_flow, &thread->nl_actions); |
30053024 BP |
1596 | if (error == ENOENT) { |
1597 | VLOG_DBG("dumped flow disappeared on get"); | |
ac64794a | 1598 | continue; |
30053024 | 1599 | } else if (error) { |
10a89ef0 BP |
1600 | VLOG_WARN("error fetching dumped flow: %s", |
1601 | ovs_strerror(error)); | |
7424fc44 | 1602 | atomic_store_relaxed(&dump->status, error); |
ac64794a | 1603 | break; |
30053024 | 1604 | } |
30053024 | 1605 | |
ac64794a BP |
1606 | /* Save this flow. Then exit, because we only have one buffer to |
1607 | * handle this case. */ | |
7af12bd7 JS |
1608 | dpif_netlink_flow_to_dpif_flow(&dpif->dpif, &flows[n_flows++], |
1609 | &datapath_flow); | |
ac64794a BP |
1610 | break; |
1611 | } | |
feebdea2 | 1612 | } |
ac64794a | 1613 | return n_flows; |
96fba48f BP |
1614 | } |
1615 | ||
eabe7c68 | 1616 | static void |
93451a0a AS |
1617 | dpif_netlink_encode_execute(int dp_ifindex, const struct dpif_execute *d_exec, |
1618 | struct ofpbuf *buf) | |
96fba48f | 1619 | { |
89625d1e | 1620 | struct ovs_header *k_exec; |
758c456d | 1621 | size_t key_ofs; |
f7cd0081 | 1622 | |
eabe7c68 | 1623 | ofpbuf_prealloc_tailroom(buf, (64 |
cf62fa4c | 1624 | + dp_packet_size(d_exec->packet) |
758c456d | 1625 | + ODP_KEY_METADATA_SIZE |
eabe7c68 | 1626 | + d_exec->actions_len)); |
f7cd0081 | 1627 | |
df2c07f4 | 1628 | nl_msg_put_genlmsghdr(buf, 0, ovs_packet_family, NLM_F_REQUEST, |
69685a88 | 1629 | OVS_PACKET_CMD_EXECUTE, OVS_PACKET_VERSION); |
f7cd0081 | 1630 | |
89625d1e BP |
1631 | k_exec = ofpbuf_put_uninit(buf, sizeof *k_exec); |
1632 | k_exec->dp_ifindex = dp_ifindex; | |
f7cd0081 | 1633 | |
89625d1e | 1634 | nl_msg_put_unspec(buf, OVS_PACKET_ATTR_PACKET, |
cf62fa4c PS |
1635 | dp_packet_data(d_exec->packet), |
1636 | dp_packet_size(d_exec->packet)); | |
758c456d JR |
1637 | |
1638 | key_ofs = nl_msg_start_nested(buf, OVS_PACKET_ATTR_KEY); | |
beb75a40 | 1639 | odp_key_from_dp_packet(buf, d_exec->packet); |
758c456d JR |
1640 | nl_msg_end_nested(buf, key_ofs); |
1641 | ||
89625d1e BP |
1642 | nl_msg_put_unspec(buf, OVS_PACKET_ATTR_ACTIONS, |
1643 | d_exec->actions, d_exec->actions_len); | |
43f9ac0a | 1644 | if (d_exec->probe) { |
2e460098 | 1645 | nl_msg_put_flag(buf, OVS_PACKET_ATTR_PROBE); |
43f9ac0a | 1646 | } |
27130224 AZ |
1647 | if (d_exec->mtu) { |
1648 | nl_msg_put_u16(buf, OVS_PACKET_ATTR_MRU, d_exec->mtu); | |
1649 | } | |
6bc60024 BP |
1650 | } |
1651 | ||
0f3358ea BP |
1652 | /* Executes, against 'dpif', up to the first 'n_ops' operations in 'ops'. |
1653 | * Returns the number actually executed (at least 1, if 'n_ops' is | |
1654 | * positive). */ | |
1655 | static size_t | |
93451a0a AS |
1656 | dpif_netlink_operate__(struct dpif_netlink *dpif, |
1657 | struct dpif_op **ops, size_t n_ops) | |
6bc60024 | 1658 | { |
0f3358ea BP |
1659 | enum { MAX_OPS = 50 }; |
1660 | ||
eabe7c68 BP |
1661 | struct op_auxdata { |
1662 | struct nl_transaction txn; | |
72d32ac0 | 1663 | |
eabe7c68 BP |
1664 | struct ofpbuf request; |
1665 | uint64_t request_stub[1024 / 8]; | |
72d32ac0 BP |
1666 | |
1667 | struct ofpbuf reply; | |
1668 | uint64_t reply_stub[1024 / 8]; | |
eabe7c68 BP |
1669 | } auxes[MAX_OPS]; |
1670 | ||
1671 | struct nl_transaction *txnsp[MAX_OPS]; | |
6bc60024 BP |
1672 | size_t i; |
1673 | ||
0f3358ea | 1674 | n_ops = MIN(n_ops, MAX_OPS); |
6bc60024 | 1675 | for (i = 0; i < n_ops; i++) { |
eabe7c68 | 1676 | struct op_auxdata *aux = &auxes[i]; |
c2b565b5 | 1677 | struct dpif_op *op = ops[i]; |
b99d3cee BP |
1678 | struct dpif_flow_put *put; |
1679 | struct dpif_flow_del *del; | |
6fe09f8c | 1680 | struct dpif_flow_get *get; |
93451a0a | 1681 | struct dpif_netlink_flow flow; |
eabe7c68 BP |
1682 | |
1683 | ofpbuf_use_stub(&aux->request, | |
1684 | aux->request_stub, sizeof aux->request_stub); | |
1685 | aux->txn.request = &aux->request; | |
b99d3cee | 1686 | |
72d32ac0 BP |
1687 | ofpbuf_use_stub(&aux->reply, aux->reply_stub, sizeof aux->reply_stub); |
1688 | aux->txn.reply = NULL; | |
1689 | ||
b99d3cee BP |
1690 | switch (op->type) { |
1691 | case DPIF_OP_FLOW_PUT: | |
1692 | put = &op->u.flow_put; | |
93451a0a | 1693 | dpif_netlink_init_flow_put(dpif, put, &flow); |
6bc60024 | 1694 | if (put->stats) { |
eabe7c68 | 1695 | flow.nlmsg_flags |= NLM_F_ECHO; |
72d32ac0 | 1696 | aux->txn.reply = &aux->reply; |
6bc60024 | 1697 | } |
93451a0a | 1698 | dpif_netlink_flow_to_ofpbuf(&flow, &aux->request); |
b99d3cee BP |
1699 | break; |
1700 | ||
1701 | case DPIF_OP_FLOW_DEL: | |
1702 | del = &op->u.flow_del; | |
93451a0a | 1703 | dpif_netlink_init_flow_del(dpif, del, &flow); |
b99d3cee | 1704 | if (del->stats) { |
eabe7c68 | 1705 | flow.nlmsg_flags |= NLM_F_ECHO; |
72d32ac0 | 1706 | aux->txn.reply = &aux->reply; |
b99d3cee | 1707 | } |
93451a0a | 1708 | dpif_netlink_flow_to_ofpbuf(&flow, &aux->request); |
b99d3cee | 1709 | break; |
6bc60024 | 1710 | |
b99d3cee | 1711 | case DPIF_OP_EXECUTE: |
0f3358ea BP |
1712 | /* Can't execute a packet that won't fit in a Netlink attribute. */ |
1713 | if (OVS_UNLIKELY(nl_attr_oversized( | |
cf62fa4c | 1714 | dp_packet_size(op->u.execute.packet)))) { |
0f3358ea BP |
1715 | /* Report an error immediately if this is the first operation. |
1716 | * Otherwise the easiest thing to do is to postpone to the next | |
1717 | * call (when this will be the first operation). */ | |
1718 | if (i == 0) { | |
1719 | VLOG_ERR_RL(&error_rl, | |
1720 | "dropping oversized %"PRIu32"-byte packet", | |
cf62fa4c | 1721 | dp_packet_size(op->u.execute.packet)); |
0f3358ea BP |
1722 | op->error = ENOBUFS; |
1723 | return 1; | |
1724 | } | |
1725 | n_ops = i; | |
1726 | } else { | |
1727 | dpif_netlink_encode_execute(dpif->dp_ifindex, &op->u.execute, | |
1728 | &aux->request); | |
1729 | } | |
b99d3cee BP |
1730 | break; |
1731 | ||
6fe09f8c JS |
1732 | case DPIF_OP_FLOW_GET: |
1733 | get = &op->u.flow_get; | |
70e5ed6f | 1734 | dpif_netlink_init_flow_get(dpif, get, &flow); |
6fe09f8c | 1735 | aux->txn.reply = get->buffer; |
93451a0a | 1736 | dpif_netlink_flow_to_ofpbuf(&flow, &aux->request); |
6fe09f8c JS |
1737 | break; |
1738 | ||
b99d3cee | 1739 | default: |
428b2edd | 1740 | OVS_NOT_REACHED(); |
6bc60024 BP |
1741 | } |
1742 | } | |
1743 | ||
6bc60024 | 1744 | for (i = 0; i < n_ops; i++) { |
eabe7c68 | 1745 | txnsp[i] = &auxes[i].txn; |
6bc60024 | 1746 | } |
a88b4e04 | 1747 | nl_transact_multiple(NETLINK_GENERIC, txnsp, n_ops); |
6bc60024 | 1748 | |
6bc60024 | 1749 | for (i = 0; i < n_ops; i++) { |
72d32ac0 | 1750 | struct op_auxdata *aux = &auxes[i]; |
eabe7c68 | 1751 | struct nl_transaction *txn = &auxes[i].txn; |
c2b565b5 | 1752 | struct dpif_op *op = ops[i]; |
b99d3cee BP |
1753 | struct dpif_flow_put *put; |
1754 | struct dpif_flow_del *del; | |
6fe09f8c | 1755 | struct dpif_flow_get *get; |
6bc60024 | 1756 | |
b99d3cee | 1757 | op->error = txn->error; |
6bc60024 | 1758 | |
b99d3cee BP |
1759 | switch (op->type) { |
1760 | case DPIF_OP_FLOW_PUT: | |
1761 | put = &op->u.flow_put; | |
cfceb2b5 | 1762 | if (put->stats) { |
b99d3cee | 1763 | if (!op->error) { |
93451a0a | 1764 | struct dpif_netlink_flow reply; |
cfceb2b5 | 1765 | |
93451a0a AS |
1766 | op->error = dpif_netlink_flow_from_ofpbuf(&reply, |
1767 | txn->reply); | |
cfceb2b5 | 1768 | if (!op->error) { |
93451a0a | 1769 | dpif_netlink_flow_get_stats(&reply, put->stats); |
cfceb2b5 BP |
1770 | } |
1771 | } | |
6bc60024 | 1772 | } |
b99d3cee BP |
1773 | break; |
1774 | ||
1775 | case DPIF_OP_FLOW_DEL: | |
1776 | del = &op->u.flow_del; | |
cfceb2b5 | 1777 | if (del->stats) { |
b99d3cee | 1778 | if (!op->error) { |
93451a0a | 1779 | struct dpif_netlink_flow reply; |
cfceb2b5 | 1780 | |
93451a0a AS |
1781 | op->error = dpif_netlink_flow_from_ofpbuf(&reply, |
1782 | txn->reply); | |
cfceb2b5 | 1783 | if (!op->error) { |
93451a0a | 1784 | dpif_netlink_flow_get_stats(&reply, del->stats); |
cfceb2b5 BP |
1785 | } |
1786 | } | |
b99d3cee BP |
1787 | } |
1788 | break; | |
1789 | ||
1790 | case DPIF_OP_EXECUTE: | |
1791 | break; | |
1792 | ||
6fe09f8c JS |
1793 | case DPIF_OP_FLOW_GET: |
1794 | get = &op->u.flow_get; | |
1795 | if (!op->error) { | |
93451a0a | 1796 | struct dpif_netlink_flow reply; |
6fe09f8c | 1797 | |
93451a0a | 1798 | op->error = dpif_netlink_flow_from_ofpbuf(&reply, txn->reply); |
6fe09f8c | 1799 | if (!op->error) { |
7af12bd7 JS |
1800 | dpif_netlink_flow_to_dpif_flow(&dpif->dpif, get->flow, |
1801 | &reply); | |
6fe09f8c JS |
1802 | } |
1803 | } | |
1804 | break; | |
1805 | ||
b99d3cee | 1806 | default: |
428b2edd | 1807 | OVS_NOT_REACHED(); |
6bc60024 BP |
1808 | } |
1809 | ||
72d32ac0 BP |
1810 | ofpbuf_uninit(&aux->request); |
1811 | ofpbuf_uninit(&aux->reply); | |
6bc60024 | 1812 | } |
0f3358ea BP |
1813 | |
1814 | return n_ops; | |
eabe7c68 BP |
1815 | } |
1816 | ||
1817 | static void | |
93451a0a | 1818 | dpif_netlink_operate(struct dpif *dpif_, struct dpif_op **ops, size_t n_ops) |
eabe7c68 | 1819 | { |
93451a0a | 1820 | struct dpif_netlink *dpif = dpif_netlink_cast(dpif_); |
9b00386b | 1821 | |
eabe7c68 | 1822 | while (n_ops > 0) { |
0f3358ea | 1823 | size_t chunk = dpif_netlink_operate__(dpif, ops, n_ops); |
eabe7c68 BP |
1824 | ops += chunk; |
1825 | n_ops -= chunk; | |
1826 | } | |
6bc60024 BP |
1827 | } |
1828 | ||
09cac43f NR |
1829 | #if _WIN32 |
1830 | static void | |
1831 | dpif_netlink_handler_uninit(struct dpif_handler *handler) | |
1832 | { | |
1833 | vport_delete_sock_pool(handler); | |
1834 | } | |
1835 | ||
1836 | static int | |
1837 | dpif_netlink_handler_init(struct dpif_handler *handler) | |
1838 | { | |
1839 | return vport_create_sock_pool(handler); | |
1840 | } | |
1841 | #else | |
1842 | ||
1843 | static int | |
1844 | dpif_netlink_handler_init(struct dpif_handler *handler) | |
1845 | { | |
1846 | handler->epoll_fd = epoll_create(10); | |
1847 | return handler->epoll_fd < 0 ? errno : 0; | |
1848 | } | |
1849 | ||
1850 | static void | |
1851 | dpif_netlink_handler_uninit(struct dpif_handler *handler) | |
1852 | { | |
1853 | close(handler->epoll_fd); | |
1854 | } | |
1855 | #endif | |
1856 | ||
1579cf67 AW |
1857 | /* Synchronizes 'channels' in 'dpif->handlers' with the set of vports |
1858 | * currently in 'dpif' in the kernel, by adding a new set of channels for | |
1859 | * any kernel vport that lacks one and deleting any channels that have no | |
1860 | * backing kernel vports. */ | |
96fba48f | 1861 | static int |
93451a0a | 1862 | dpif_netlink_refresh_channels(struct dpif_netlink *dpif, uint32_t n_handlers) |
b90de034 | 1863 | OVS_REQ_WRLOCK(dpif->upcall_lock) |
96fba48f | 1864 | { |
8381a3d3 | 1865 | unsigned long int *keep_channels; |
93451a0a | 1866 | struct dpif_netlink_vport vport; |
8381a3d3 BP |
1867 | size_t keep_channels_nbits; |
1868 | struct nl_dump dump; | |
d57695d7 JS |
1869 | uint64_t reply_stub[NL_DUMP_BUFSIZE / 8]; |
1870 | struct ofpbuf buf; | |
8381a3d3 BP |
1871 | int retval = 0; |
1872 | size_t i; | |
982b8810 | 1873 | |
09cac43f NR |
1874 | ovs_assert(!WINDOWS || n_handlers <= 1); |
1875 | ovs_assert(!WINDOWS || dpif->n_handlers <= 1); | |
1876 | ||
1579cf67 AW |
1877 | if (dpif->n_handlers != n_handlers) { |
1878 | destroy_all_channels(dpif); | |
1879 | dpif->handlers = xzalloc(n_handlers * sizeof *dpif->handlers); | |
1880 | for (i = 0; i < n_handlers; i++) { | |
09cac43f | 1881 | int error; |
1579cf67 AW |
1882 | struct dpif_handler *handler = &dpif->handlers[i]; |
1883 | ||
09cac43f NR |
1884 | error = dpif_netlink_handler_init(handler); |
1885 | if (error) { | |
1579cf67 AW |
1886 | size_t j; |
1887 | ||
1888 | for (j = 0; j < i; j++) { | |
aa5c0216 | 1889 | struct dpif_handler *tmp = &dpif->handlers[j]; |
09cac43f | 1890 | dpif_netlink_handler_uninit(tmp); |
1579cf67 AW |
1891 | } |
1892 | free(dpif->handlers); | |
1893 | dpif->handlers = NULL; | |
1894 | ||
09cac43f | 1895 | return error; |
1579cf67 | 1896 | } |
8381a3d3 | 1897 | } |
1579cf67 AW |
1898 | dpif->n_handlers = n_handlers; |
1899 | } | |
1900 | ||
1901 | for (i = 0; i < n_handlers; i++) { | |
1902 | struct dpif_handler *handler = &dpif->handlers[i]; | |
1903 | ||
1904 | handler->event_offset = handler->n_events = 0; | |
17411ecf | 1905 | } |
b063d9f0 | 1906 | |
8381a3d3 BP |
1907 | keep_channels_nbits = dpif->uc_array_size; |
1908 | keep_channels = bitmap_allocate(keep_channels_nbits); | |
982b8810 | 1909 | |
d57695d7 | 1910 | ofpbuf_use_stub(&buf, reply_stub, sizeof reply_stub); |
93451a0a AS |
1911 | dpif_netlink_port_dump_start__(dpif, &dump); |
1912 | while (!dpif_netlink_port_dump_next__(dpif, &dump, &vport, &buf)) { | |
8381a3d3 | 1913 | uint32_t port_no = odp_to_u32(vport.port_no); |
1579cf67 | 1914 | uint32_t *upcall_pids = NULL; |
8381a3d3 | 1915 | int error; |
50f80534 | 1916 | |
1579cf67 AW |
1917 | if (port_no >= dpif->uc_array_size |
1918 | || !vport_get_pids(dpif, port_no, &upcall_pids)) { | |
09cac43f | 1919 | struct nl_sock **socksp = vport_create_socksp(dpif, &error); |
1579cf67 AW |
1920 | |
1921 | if (!socksp) { | |
1922 | goto error; | |
1923 | } | |
1924 | ||
1925 | error = vport_add_channels(dpif, vport.port_no, socksp); | |
b063d9f0 | 1926 | if (error) { |
1579cf67 | 1927 | VLOG_INFO("%s: could not add channels for port %s", |
9b00386b | 1928 | dpif_name(&dpif->dpif), vport.name); |
09cac43f | 1929 | vport_del_socksp(dpif, socksp); |
8381a3d3 BP |
1930 | retval = error; |
1931 | goto error; | |
982b8810 | 1932 | } |
1579cf67 AW |
1933 | upcall_pids = vport_socksp_to_pids(socksp, dpif->n_handlers); |
1934 | free(socksp); | |
8381a3d3 | 1935 | } |
50f80534 | 1936 | |
8381a3d3 | 1937 | /* Configure the vport to deliver misses to 'sock'. */ |
1579cf67 AW |
1938 | if (vport.upcall_pids[0] == 0 |
1939 | || vport.n_upcall_pids != dpif->n_handlers | |
1940 | || memcmp(upcall_pids, vport.upcall_pids, n_handlers * sizeof | |
1941 | *upcall_pids)) { | |
93451a0a | 1942 | struct dpif_netlink_vport vport_request; |
989fd548 | 1943 | |
93451a0a | 1944 | dpif_netlink_vport_init(&vport_request); |
989fd548 JP |
1945 | vport_request.cmd = OVS_VPORT_CMD_SET; |
1946 | vport_request.dp_ifindex = dpif->dp_ifindex; | |
8381a3d3 | 1947 | vport_request.port_no = vport.port_no; |
1579cf67 AW |
1948 | vport_request.n_upcall_pids = dpif->n_handlers; |
1949 | vport_request.upcall_pids = upcall_pids; | |
93451a0a | 1950 | error = dpif_netlink_vport_transact(&vport_request, NULL, NULL); |
1579cf67 | 1951 | if (error) { |
989fd548 JP |
1952 | VLOG_WARN_RL(&error_rl, |
1953 | "%s: failed to set upcall pid on port: %s", | |
10a89ef0 | 1954 | dpif_name(&dpif->dpif), ovs_strerror(error)); |
989fd548 | 1955 | |
8381a3d3 BP |
1956 | if (error != ENODEV && error != ENOENT) { |
1957 | retval = error; | |
989fd548 | 1958 | } else { |
8381a3d3 BP |
1959 | /* The vport isn't really there, even though the dump says |
1960 | * it is. Probably we just hit a race after a port | |
1961 | * disappeared. */ | |
989fd548 | 1962 | } |
8381a3d3 | 1963 | goto error; |
50f80534 | 1964 | } |
8381a3d3 | 1965 | } |
14b4d2f9 | 1966 | |
8381a3d3 BP |
1967 | if (port_no < keep_channels_nbits) { |
1968 | bitmap_set1(keep_channels, port_no); | |
1969 | } | |
1579cf67 | 1970 | free(upcall_pids); |
8381a3d3 BP |
1971 | continue; |
1972 | ||
1973 | error: | |
1579cf67 AW |
1974 | free(upcall_pids); |
1975 | vport_del_channels(dpif, vport.port_no); | |
982b8810 | 1976 | } |
8381a3d3 | 1977 | nl_dump_done(&dump); |
d57695d7 | 1978 | ofpbuf_uninit(&buf); |
b063d9f0 | 1979 | |
8381a3d3 BP |
1980 | /* Discard any saved channels that we didn't reuse. */ |
1981 | for (i = 0; i < keep_channels_nbits; i++) { | |
1982 | if (!bitmap_is_set(keep_channels, i)) { | |
1579cf67 | 1983 | vport_del_channels(dpif, u32_to_odp(i)); |
8381a3d3 BP |
1984 | } |
1985 | } | |
1986 | free(keep_channels); | |
1987 | ||
1988 | return retval; | |
1989 | } | |
1990 | ||
1991 | static int | |
93451a0a | 1992 | dpif_netlink_recv_set__(struct dpif_netlink *dpif, bool enable) |
b90de034 | 1993 | OVS_REQ_WRLOCK(dpif->upcall_lock) |
8381a3d3 | 1994 | { |
1579cf67 | 1995 | if ((dpif->handlers != NULL) == enable) { |
8381a3d3 BP |
1996 | return 0; |
1997 | } else if (!enable) { | |
1579cf67 | 1998 | destroy_all_channels(dpif); |
8381a3d3 BP |
1999 | return 0; |
2000 | } else { | |
93451a0a | 2001 | return dpif_netlink_refresh_channels(dpif, 1); |
8381a3d3 | 2002 | } |
96fba48f BP |
2003 | } |
2004 | ||
9fafa796 | 2005 | static int |
93451a0a | 2006 | dpif_netlink_recv_set(struct dpif *dpif_, bool enable) |
9fafa796 | 2007 | { |
93451a0a | 2008 | struct dpif_netlink *dpif = dpif_netlink_cast(dpif_); |
9fafa796 BP |
2009 | int error; |
2010 | ||
1579cf67 | 2011 | fat_rwlock_wrlock(&dpif->upcall_lock); |
93451a0a | 2012 | error = dpif_netlink_recv_set__(dpif, enable); |
1579cf67 | 2013 | fat_rwlock_unlock(&dpif->upcall_lock); |
9fafa796 BP |
2014 | |
2015 | return error; | |
2016 | } | |
2017 | ||
1954e6bb | 2018 | static int |
93451a0a | 2019 | dpif_netlink_handlers_set(struct dpif *dpif_, uint32_t n_handlers) |
1954e6bb | 2020 | { |
93451a0a | 2021 | struct dpif_netlink *dpif = dpif_netlink_cast(dpif_); |
1579cf67 AW |
2022 | int error = 0; |
2023 | ||
09cac43f NR |
2024 | #ifdef _WIN32 |
2025 | /* Multiple upcall handlers will be supported once kernel datapath supports | |
2026 | * it. */ | |
2027 | if (n_handlers > 1) { | |
2028 | return error; | |
2029 | } | |
2030 | #endif | |
2031 | ||
1579cf67 AW |
2032 | fat_rwlock_wrlock(&dpif->upcall_lock); |
2033 | if (dpif->handlers) { | |
93451a0a | 2034 | error = dpif_netlink_refresh_channels(dpif, n_handlers); |
1579cf67 AW |
2035 | } |
2036 | fat_rwlock_unlock(&dpif->upcall_lock); | |
2037 | ||
2038 | return error; | |
1954e6bb AW |
2039 | } |
2040 | ||
aae51f53 | 2041 | static int |
93451a0a | 2042 | dpif_netlink_queue_to_priority(const struct dpif *dpif OVS_UNUSED, |
aae51f53 BP |
2043 | uint32_t queue_id, uint32_t *priority) |
2044 | { | |
2045 | if (queue_id < 0xf000) { | |
17ee3c1f | 2046 | *priority = TC_H_MAKE(1 << 16, queue_id + 1); |
aae51f53 BP |
2047 | return 0; |
2048 | } else { | |
2049 | return EINVAL; | |
2050 | } | |
2051 | } | |
2052 | ||
96fba48f | 2053 | static int |
7af12bd7 JS |
2054 | parse_odp_packet(const struct dpif_netlink *dpif, struct ofpbuf *buf, |
2055 | struct dpif_upcall *upcall, int *dp_ifindex) | |
856081f6 | 2056 | { |
df2c07f4 | 2057 | static const struct nl_policy ovs_packet_policy[] = { |
856081f6 | 2058 | /* Always present. */ |
df2c07f4 | 2059 | [OVS_PACKET_ATTR_PACKET] = { .type = NL_A_UNSPEC, |
856081f6 | 2060 | .min_len = ETH_HEADER_LEN }, |
df2c07f4 | 2061 | [OVS_PACKET_ATTR_KEY] = { .type = NL_A_NESTED }, |
856081f6 | 2062 | |
df2c07f4 | 2063 | /* OVS_PACKET_CMD_ACTION only. */ |
e995e3df | 2064 | [OVS_PACKET_ATTR_USERDATA] = { .type = NL_A_UNSPEC, .optional = true }, |
8b7ea2d4 | 2065 | [OVS_PACKET_ATTR_EGRESS_TUN_KEY] = { .type = NL_A_NESTED, .optional = true }, |
7321bda3 | 2066 | [OVS_PACKET_ATTR_ACTIONS] = { .type = NL_A_NESTED, .optional = true }, |
27130224 | 2067 | [OVS_PACKET_ATTR_MRU] = { .type = NL_A_U16, .optional = true } |
856081f6 BP |
2068 | }; |
2069 | ||
0a2869d5 BP |
2070 | struct ofpbuf b = ofpbuf_const_initializer(buf->data, buf->size); |
2071 | struct nlmsghdr *nlmsg = ofpbuf_try_pull(&b, sizeof *nlmsg); | |
2072 | struct genlmsghdr *genl = ofpbuf_try_pull(&b, sizeof *genl); | |
2073 | struct ovs_header *ovs_header = ofpbuf_try_pull(&b, sizeof *ovs_header); | |
982b8810 | 2074 | |
0a2869d5 | 2075 | struct nlattr *a[ARRAY_SIZE(ovs_packet_policy)]; |
df2c07f4 JP |
2076 | if (!nlmsg || !genl || !ovs_header |
2077 | || nlmsg->nlmsg_type != ovs_packet_family | |
2078 | || !nl_policy_parse(&b, 0, ovs_packet_policy, a, | |
2079 | ARRAY_SIZE(ovs_packet_policy))) { | |
856081f6 BP |
2080 | return EINVAL; |
2081 | } | |
2082 | ||
0a2869d5 BP |
2083 | int type = (genl->cmd == OVS_PACKET_CMD_MISS ? DPIF_UC_MISS |
2084 | : genl->cmd == OVS_PACKET_CMD_ACTION ? DPIF_UC_ACTION | |
2085 | : -1); | |
aaff4b55 BP |
2086 | if (type < 0) { |
2087 | return EINVAL; | |
2088 | } | |
82272ede | 2089 | |
877c9270 | 2090 | /* (Re)set ALL fields of '*upcall' on successful return. */ |
aaff4b55 | 2091 | upcall->type = type; |
ebc56baa BP |
2092 | upcall->key = CONST_CAST(struct nlattr *, |
2093 | nl_attr_get(a[OVS_PACKET_ATTR_KEY])); | |
df2c07f4 | 2094 | upcall->key_len = nl_attr_get_size(a[OVS_PACKET_ATTR_KEY]); |
7af12bd7 | 2095 | dpif_flow_hash(&dpif->dpif, upcall->key, upcall->key_len, &upcall->ufid); |
e995e3df | 2096 | upcall->userdata = a[OVS_PACKET_ATTR_USERDATA]; |
8b7ea2d4 | 2097 | upcall->out_tun_key = a[OVS_PACKET_ATTR_EGRESS_TUN_KEY]; |
7321bda3 | 2098 | upcall->actions = a[OVS_PACKET_ATTR_ACTIONS]; |
27130224 | 2099 | upcall->mru = a[OVS_PACKET_ATTR_MRU]; |
da546e07 JR |
2100 | |
2101 | /* Allow overwriting the netlink attribute header without reallocating. */ | |
cf62fa4c | 2102 | dp_packet_use_stub(&upcall->packet, |
da546e07 JR |
2103 | CONST_CAST(struct nlattr *, |
2104 | nl_attr_get(a[OVS_PACKET_ATTR_PACKET])) - 1, | |
2105 | nl_attr_get_size(a[OVS_PACKET_ATTR_PACKET]) + | |
2106 | sizeof(struct nlattr)); | |
cf62fa4c PS |
2107 | dp_packet_set_data(&upcall->packet, |
2108 | (char *)dp_packet_data(&upcall->packet) + sizeof(struct nlattr)); | |
2109 | dp_packet_set_size(&upcall->packet, nl_attr_get_size(a[OVS_PACKET_ATTR_PACKET])); | |
da546e07 | 2110 | |
2482b0b0 JS |
2111 | if (nl_attr_find__(upcall->key, upcall->key_len, OVS_KEY_ATTR_ETHERNET)) { |
2112 | /* Ethernet frame */ | |
2113 | upcall->packet.packet_type = htonl(PT_ETH); | |
2114 | } else { | |
2115 | /* Non-Ethernet packet. Get the Ethertype from the NL attributes */ | |
2116 | ovs_be16 ethertype = 0; | |
2117 | const struct nlattr *et_nla = nl_attr_find__(upcall->key, | |
2118 | upcall->key_len, | |
2119 | OVS_KEY_ATTR_ETHERTYPE); | |
2120 | if (et_nla) { | |
2121 | ethertype = nl_attr_get_be16(et_nla); | |
2122 | } | |
2123 | upcall->packet.packet_type = PACKET_TYPE_BE(OFPHTN_ETHERTYPE, | |
2124 | ntohs(ethertype)); | |
2125 | dp_packet_set_l3(&upcall->packet, dp_packet_data(&upcall->packet)); | |
2126 | } | |
2127 | ||
df2c07f4 | 2128 | *dp_ifindex = ovs_header->dp_ifindex; |
982b8810 | 2129 | |
856081f6 BP |
2130 | return 0; |
2131 | } | |
2132 | ||
09cac43f NR |
2133 | #ifdef _WIN32 |
2134 | #define PACKET_RECV_BATCH_SIZE 50 | |
2135 | static int | |
2136 | dpif_netlink_recv_windows(struct dpif_netlink *dpif, uint32_t handler_id, | |
2137 | struct dpif_upcall *upcall, struct ofpbuf *buf) | |
2138 | OVS_REQ_RDLOCK(dpif->upcall_lock) | |
2139 | { | |
2140 | struct dpif_handler *handler; | |
2141 | int read_tries = 0; | |
2142 | struct dpif_windows_vport_sock *sock_pool; | |
2143 | uint32_t i; | |
2144 | ||
2145 | if (!dpif->handlers) { | |
2146 | return EAGAIN; | |
2147 | } | |
2148 | ||
2149 | /* Only one handler is supported currently. */ | |
2150 | if (handler_id >= 1) { | |
2151 | return EAGAIN; | |
2152 | } | |
2153 | ||
2154 | if (handler_id >= dpif->n_handlers) { | |
2155 | return EAGAIN; | |
2156 | } | |
2157 | ||
2158 | handler = &dpif->handlers[handler_id]; | |
2159 | sock_pool = handler->vport_sock_pool; | |
2160 | ||
2161 | for (i = 0; i < VPORT_SOCK_POOL_SIZE; i++) { | |
2162 | for (;;) { | |
2163 | int dp_ifindex; | |
2164 | int error; | |
2165 | ||
2166 | if (++read_tries > PACKET_RECV_BATCH_SIZE) { | |
2167 | return EAGAIN; | |
2168 | } | |
2169 | ||
2170 | error = nl_sock_recv(sock_pool[i].nl_sock, buf, false); | |
2171 | if (error == ENOBUFS) { | |
2172 | /* ENOBUFS typically means that we've received so many | |
2173 | * packets that the buffer overflowed. Try again | |
2174 | * immediately because there's almost certainly a packet | |
2175 | * waiting for us. */ | |
2176 | /* XXX: report_loss(dpif, ch, idx, handler_id); */ | |
2177 | continue; | |
2178 | } | |
2179 | ||
2180 | /* XXX: ch->last_poll = time_msec(); */ | |
2181 | if (error) { | |
2182 | if (error == EAGAIN) { | |
2183 | break; | |
2184 | } | |
2185 | return error; | |
2186 | } | |
2187 | ||
27edb4aa | 2188 | error = parse_odp_packet(dpif, buf, upcall, &dp_ifindex); |
09cac43f NR |
2189 | if (!error && dp_ifindex == dpif->dp_ifindex) { |
2190 | return 0; | |
2191 | } else if (error) { | |
2192 | return error; | |
2193 | } | |
2194 | } | |
2195 | } | |
2196 | ||
2197 | return EAGAIN; | |
2198 | } | |
2199 | #else | |
856081f6 | 2200 | static int |
93451a0a AS |
2201 | dpif_netlink_recv__(struct dpif_netlink *dpif, uint32_t handler_id, |
2202 | struct dpif_upcall *upcall, struct ofpbuf *buf) | |
b90de034 | 2203 | OVS_REQ_RDLOCK(dpif->upcall_lock) |
96fba48f | 2204 | { |
1579cf67 | 2205 | struct dpif_handler *handler; |
17411ecf | 2206 | int read_tries = 0; |
96fba48f | 2207 | |
1579cf67 AW |
2208 | if (!dpif->handlers || handler_id >= dpif->n_handlers) { |
2209 | return EAGAIN; | |
982b8810 BP |
2210 | } |
2211 | ||
1579cf67 AW |
2212 | handler = &dpif->handlers[handler_id]; |
2213 | if (handler->event_offset >= handler->n_events) { | |
8522ba09 | 2214 | int retval; |
989fd548 | 2215 | |
1579cf67 | 2216 | handler->event_offset = handler->n_events = 0; |
f6d1465c | 2217 | |
8522ba09 | 2218 | do { |
1579cf67 | 2219 | retval = epoll_wait(handler->epoll_fd, handler->epoll_events, |
989fd548 | 2220 | dpif->uc_array_size, 0); |
8522ba09 | 2221 | } while (retval < 0 && errno == EINTR); |
09cac43f | 2222 | |
8522ba09 BP |
2223 | if (retval < 0) { |
2224 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); | |
10a89ef0 | 2225 | VLOG_WARN_RL(&rl, "epoll_wait failed (%s)", ovs_strerror(errno)); |
989fd548 | 2226 | } else if (retval > 0) { |
1579cf67 | 2227 | handler->n_events = retval; |
8522ba09 | 2228 | } |
8522ba09 BP |
2229 | } |
2230 | ||
1579cf67 AW |
2231 | while (handler->event_offset < handler->n_events) { |
2232 | int idx = handler->epoll_events[handler->event_offset].data.u32; | |
2233 | struct dpif_channel *ch = &dpif->handlers[handler_id].channels[idx]; | |
8522ba09 | 2234 | |
1579cf67 | 2235 | handler->event_offset++; |
17411ecf | 2236 | |
f6d1465c | 2237 | for (;;) { |
8522ba09 | 2238 | int dp_ifindex; |
f6d1465c | 2239 | int error; |
17411ecf | 2240 | |
f6d1465c BP |
2241 | if (++read_tries > 50) { |
2242 | return EAGAIN; | |
2243 | } | |
17411ecf | 2244 | |
fe3d61b3 | 2245 | error = nl_sock_recv(ch->sock, buf, false); |
14b4d2f9 BP |
2246 | if (error == ENOBUFS) { |
2247 | /* ENOBUFS typically means that we've received so many | |
2248 | * packets that the buffer overflowed. Try again | |
2249 | * immediately because there's almost certainly a packet | |
2250 | * waiting for us. */ | |
9b00386b | 2251 | report_loss(dpif, ch, idx, handler_id); |
14b4d2f9 BP |
2252 | continue; |
2253 | } | |
2254 | ||
2255 | ch->last_poll = time_msec(); | |
72d32ac0 | 2256 | if (error) { |
72d32ac0 BP |
2257 | if (error == EAGAIN) { |
2258 | break; | |
2259 | } | |
f6d1465c BP |
2260 | return error; |
2261 | } | |
17411ecf | 2262 | |
7af12bd7 | 2263 | error = parse_odp_packet(dpif, buf, upcall, &dp_ifindex); |
a12b3ead | 2264 | if (!error && dp_ifindex == dpif->dp_ifindex) { |
f6d1465c | 2265 | return 0; |
989fd548 | 2266 | } else if (error) { |
f6d1465c | 2267 | return error; |
17411ecf | 2268 | } |
982b8810 | 2269 | } |
50f80534 | 2270 | } |
982b8810 BP |
2271 | |
2272 | return EAGAIN; | |
96fba48f | 2273 | } |
09cac43f | 2274 | #endif |
96fba48f | 2275 | |
9fafa796 | 2276 | static int |
93451a0a AS |
2277 | dpif_netlink_recv(struct dpif *dpif_, uint32_t handler_id, |
2278 | struct dpif_upcall *upcall, struct ofpbuf *buf) | |
9fafa796 | 2279 | { |
93451a0a | 2280 | struct dpif_netlink *dpif = dpif_netlink_cast(dpif_); |
9fafa796 BP |
2281 | int error; |
2282 | ||
1579cf67 | 2283 | fat_rwlock_rdlock(&dpif->upcall_lock); |
09cac43f NR |
2284 | #ifdef _WIN32 |
2285 | error = dpif_netlink_recv_windows(dpif, handler_id, upcall, buf); | |
2286 | #else | |
93451a0a | 2287 | error = dpif_netlink_recv__(dpif, handler_id, upcall, buf); |
09cac43f | 2288 | #endif |
1579cf67 | 2289 | fat_rwlock_unlock(&dpif->upcall_lock); |
9fafa796 BP |
2290 | |
2291 | return error; | |
2292 | } | |
2293 | ||
96fba48f | 2294 | static void |
93451a0a | 2295 | dpif_netlink_recv_wait__(struct dpif_netlink *dpif, uint32_t handler_id) |
b90de034 | 2296 | OVS_REQ_RDLOCK(dpif->upcall_lock) |
96fba48f | 2297 | { |
93451a0a | 2298 | #ifdef _WIN32 |
09cac43f NR |
2299 | uint32_t i; |
2300 | struct dpif_windows_vport_sock *sock_pool = | |
2301 | dpif->handlers[handler_id].vport_sock_pool; | |
2302 | ||
2303 | /* Only one handler is supported currently. */ | |
2304 | if (handler_id >= 1) { | |
2305 | return; | |
2306 | } | |
2307 | ||
2308 | for (i = 0; i < VPORT_SOCK_POOL_SIZE; i++) { | |
2309 | nl_sock_wait(sock_pool[i].nl_sock, POLLIN); | |
2310 | } | |
93451a0a | 2311 | #else |
1579cf67 AW |
2312 | if (dpif->handlers && handler_id < dpif->n_handlers) { |
2313 | struct dpif_handler *handler = &dpif->handlers[handler_id]; | |
2314 | ||
2315 | poll_fd_wait(handler->epoll_fd, POLLIN); | |
17411ecf | 2316 | } |
93451a0a | 2317 | #endif |
96fba48f BP |
2318 | } |
2319 | ||
1ba530f4 | 2320 | static void |
93451a0a | 2321 | dpif_netlink_recv_wait(struct dpif *dpif_, uint32_t handler_id) |
1ba530f4 | 2322 | { |
93451a0a | 2323 | struct dpif_netlink *dpif = dpif_netlink_cast(dpif_); |
17411ecf | 2324 | |
b90de034 | 2325 | fat_rwlock_rdlock(&dpif->upcall_lock); |
93451a0a | 2326 | dpif_netlink_recv_wait__(dpif, handler_id); |
b90de034 AW |
2327 | fat_rwlock_unlock(&dpif->upcall_lock); |
2328 | } | |
2329 | ||
2330 | static void | |
93451a0a | 2331 | dpif_netlink_recv_purge__(struct dpif_netlink *dpif) |
b90de034 AW |
2332 | OVS_REQ_WRLOCK(dpif->upcall_lock) |
2333 | { | |
1579cf67 AW |
2334 | if (dpif->handlers) { |
2335 | size_t i, j; | |
2336 | ||
2337 | for (i = 0; i < dpif->uc_array_size; i++ ) { | |
2338 | if (!dpif->handlers[0].channels[i].sock) { | |
2339 | continue; | |
2340 | } | |
1ba530f4 | 2341 | |
1579cf67 AW |
2342 | for (j = 0; j < dpif->n_handlers; j++) { |
2343 | nl_sock_drain(dpif->handlers[j].channels[i].sock); | |
9fafa796 | 2344 | } |
989fd548 | 2345 | } |
1ba530f4 | 2346 | } |
b90de034 AW |
2347 | } |
2348 | ||
2349 | static void | |
93451a0a | 2350 | dpif_netlink_recv_purge(struct dpif *dpif_) |
b90de034 | 2351 | { |
93451a0a | 2352 | struct dpif_netlink *dpif = dpif_netlink_cast(dpif_); |
b90de034 AW |
2353 | |
2354 | fat_rwlock_wrlock(&dpif->upcall_lock); | |
93451a0a | 2355 | dpif_netlink_recv_purge__(dpif); |
1579cf67 | 2356 | fat_rwlock_unlock(&dpif->upcall_lock); |
1ba530f4 BP |
2357 | } |
2358 | ||
b5cbbcf6 AZ |
2359 | static char * |
2360 | dpif_netlink_get_datapath_version(void) | |
2361 | { | |
2362 | char *version_str = NULL; | |
2363 | ||
2364 | #ifdef __linux__ | |
2365 | ||
2366 | #define MAX_VERSION_STR_SIZE 80 | |
2367 | #define LINUX_DATAPATH_VERSION_FILE "/sys/module/openvswitch/version" | |
2368 | FILE *f; | |
2369 | ||
2370 | f = fopen(LINUX_DATAPATH_VERSION_FILE, "r"); | |
2371 | if (f) { | |
2372 | char *newline; | |
2373 | char version[MAX_VERSION_STR_SIZE]; | |
2374 | ||
2375 | if (fgets(version, MAX_VERSION_STR_SIZE, f)) { | |
2376 | newline = strchr(version, '\n'); | |
2377 | if (newline) { | |
2378 | *newline = '\0'; | |
2379 | } | |
2380 | version_str = xstrdup(version); | |
2381 | } | |
2382 | fclose(f); | |
2383 | } | |
2384 | #endif | |
2385 | ||
2386 | return version_str; | |
2387 | } | |
2388 | ||
c11c9f4a DDP |
2389 | struct dpif_netlink_ct_dump_state { |
2390 | struct ct_dpif_dump_state up; | |
2391 | struct nl_ct_dump_state *nl_ct_dump; | |
2392 | }; | |
2393 | ||
2394 | static int | |
2395 | dpif_netlink_ct_dump_start(struct dpif *dpif OVS_UNUSED, | |
2396 | struct ct_dpif_dump_state **dump_, | |
2397 | const uint16_t *zone) | |
2398 | { | |
2399 | struct dpif_netlink_ct_dump_state *dump; | |
2400 | int err; | |
2401 | ||
2402 | dump = xzalloc(sizeof *dump); | |
2403 | err = nl_ct_dump_start(&dump->nl_ct_dump, zone); | |
2404 | if (err) { | |
2405 | free(dump); | |
2406 | return err; | |
2407 | } | |
2408 | ||
2409 | *dump_ = &dump->up; | |
2410 | ||
2411 | return 0; | |
2412 | } | |
2413 | ||
2414 | static int | |
2415 | dpif_netlink_ct_dump_next(struct dpif *dpif OVS_UNUSED, | |
2416 | struct ct_dpif_dump_state *dump_, | |
2417 | struct ct_dpif_entry *entry) | |
2418 | { | |
2419 | struct dpif_netlink_ct_dump_state *dump; | |
2420 | ||
2421 | INIT_CONTAINER(dump, dump_, up); | |
2422 | ||
2423 | return nl_ct_dump_next(dump->nl_ct_dump, entry); | |
2424 | } | |
2425 | ||
2426 | static int | |
2427 | dpif_netlink_ct_dump_done(struct dpif *dpif OVS_UNUSED, | |
2428 | struct ct_dpif_dump_state *dump_) | |
2429 | { | |
2430 | struct dpif_netlink_ct_dump_state *dump; | |
2431 | int err; | |
2432 | ||
2433 | INIT_CONTAINER(dump, dump_, up); | |
2434 | ||
2435 | err = nl_ct_dump_done(dump->nl_ct_dump); | |
2436 | free(dump); | |
2437 | return err; | |
2438 | } | |
15eabc97 DDP |
2439 | |
2440 | static int | |
2441 | dpif_netlink_ct_flush(struct dpif *dpif OVS_UNUSED, const uint16_t *zone) | |
2442 | { | |
2443 | if (zone) { | |
2444 | return nl_ct_flush_zone(*zone); | |
2445 | } else { | |
2446 | return nl_ct_flush(); | |
2447 | } | |
2448 | } | |
c11c9f4a | 2449 | |
5dddf960 JR |
2450 | \f |
2451 | /* Meters */ | |
2452 | static void | |
2453 | dpif_netlink_meter_get_features(const struct dpif * dpif OVS_UNUSED, | |
2454 | struct ofputil_meter_features *features) | |
2455 | { | |
2456 | features->max_meters = 0; | |
2457 | features->band_types = 0; | |
2458 | features->capabilities = 0; | |
2459 | features->max_bands = 0; | |
2460 | features->max_color = 0; | |
2461 | } | |
2462 | ||
2463 | static int | |
2464 | dpif_netlink_meter_set(struct dpif *dpif OVS_UNUSED, | |
2465 | ofproto_meter_id *meter_id OVS_UNUSED, | |
2466 | struct ofputil_meter_config *config OVS_UNUSED) | |
2467 | { | |
2468 | return EFBIG; /* meter_id out of range */ | |
2469 | } | |
2470 | ||
2471 | static int | |
2472 | dpif_netlink_meter_get(const struct dpif *dpif OVS_UNUSED, | |
2473 | ofproto_meter_id meter_id OVS_UNUSED, | |
2474 | struct ofputil_meter_stats *stats OVS_UNUSED, | |
2475 | uint16_t n_bands OVS_UNUSED) | |
2476 | { | |
2477 | return EFBIG; /* meter_id out of range */ | |
2478 | } | |
2479 | ||
2480 | static int | |
2481 | dpif_netlink_meter_del(struct dpif *dpif OVS_UNUSED, | |
2482 | ofproto_meter_id meter_id OVS_UNUSED, | |
2483 | struct ofputil_meter_stats *stats OVS_UNUSED, | |
2484 | uint16_t n_bands OVS_UNUSED) | |
2485 | { | |
2486 | return EFBIG; /* meter_id out of range */ | |
2487 | } | |
2488 | ||
2489 | \f | |
93451a0a | 2490 | const struct dpif_class dpif_netlink_class = { |
1a6f1e2a | 2491 | "system", |
c8973eb6 | 2492 | NULL, /* init */ |
93451a0a | 2493 | dpif_netlink_enumerate, |
0aeaabc8 | 2494 | NULL, |
93451a0a AS |
2495 | dpif_netlink_open, |
2496 | dpif_netlink_close, | |
2497 | dpif_netlink_destroy, | |
2498 | dpif_netlink_run, | |
e4516b20 | 2499 | NULL, /* wait */ |
93451a0a AS |
2500 | dpif_netlink_get_stats, |
2501 | dpif_netlink_port_add, | |
2502 | dpif_netlink_port_del, | |
91364d18 | 2503 | NULL, /* port_set_config */ |
93451a0a AS |
2504 | dpif_netlink_port_query_by_number, |
2505 | dpif_netlink_port_query_by_name, | |
2506 | dpif_netlink_port_get_pid, | |
2507 | dpif_netlink_port_dump_start, | |
2508 | dpif_netlink_port_dump_next, | |
2509 | dpif_netlink_port_dump_done, | |
2510 | dpif_netlink_port_poll, | |
2511 | dpif_netlink_port_poll_wait, | |
2512 | dpif_netlink_flow_flush, | |
2513 | dpif_netlink_flow_dump_create, | |
2514 | dpif_netlink_flow_dump_destroy, | |
2515 | dpif_netlink_flow_dump_thread_create, | |
2516 | dpif_netlink_flow_dump_thread_destroy, | |
2517 | dpif_netlink_flow_dump_next, | |
2518 | dpif_netlink_operate, | |
2519 | dpif_netlink_recv_set, | |
2520 | dpif_netlink_handlers_set, | |
d4f6865c | 2521 | NULL, /* set_config */ |
93451a0a AS |
2522 | dpif_netlink_queue_to_priority, |
2523 | dpif_netlink_recv, | |
2524 | dpif_netlink_recv_wait, | |
2525 | dpif_netlink_recv_purge, | |
e4e74c3a | 2526 | NULL, /* register_dp_purge_cb */ |
6b31e073 RW |
2527 | NULL, /* register_upcall_cb */ |
2528 | NULL, /* enable_upcall */ | |
2529 | NULL, /* disable_upcall */ | |
b5cbbcf6 | 2530 | dpif_netlink_get_datapath_version, /* get_datapath_version */ |
c11c9f4a DDP |
2531 | dpif_netlink_ct_dump_start, |
2532 | dpif_netlink_ct_dump_next, | |
2533 | dpif_netlink_ct_dump_done, | |
5dddf960 JR |
2534 | dpif_netlink_ct_flush, |
2535 | dpif_netlink_meter_get_features, | |
2536 | dpif_netlink_meter_set, | |
2537 | dpif_netlink_meter_get, | |
2538 | dpif_netlink_meter_del, | |
96fba48f | 2539 | }; |
93451a0a | 2540 | |
96fba48f | 2541 | static int |
93451a0a | 2542 | dpif_netlink_init(void) |
96fba48f | 2543 | { |
eb8ed438 BP |
2544 | static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; |
2545 | static int error; | |
982b8810 | 2546 | |
eb8ed438 | 2547 | if (ovsthread_once_start(&once)) { |
df2c07f4 JP |
2548 | error = nl_lookup_genl_family(OVS_DATAPATH_FAMILY, |
2549 | &ovs_datapath_family); | |
37a1300c | 2550 | if (error) { |
cae7529c CL |
2551 | VLOG_WARN("Generic Netlink family '%s' does not exist. " |
2552 | "The Open vSwitch kernel module is probably not loaded.", | |
2553 | OVS_DATAPATH_FAMILY); | |
37a1300c | 2554 | } |
f0fef760 | 2555 | if (!error) { |
df2c07f4 | 2556 | error = nl_lookup_genl_family(OVS_VPORT_FAMILY, &ovs_vport_family); |
f0fef760 | 2557 | } |
37a1300c | 2558 | if (!error) { |
df2c07f4 | 2559 | error = nl_lookup_genl_family(OVS_FLOW_FAMILY, &ovs_flow_family); |
37a1300c | 2560 | } |
aaff4b55 | 2561 | if (!error) { |
df2c07f4 JP |
2562 | error = nl_lookup_genl_family(OVS_PACKET_FAMILY, |
2563 | &ovs_packet_family); | |
aaff4b55 | 2564 | } |
c7178a0b EJ |
2565 | if (!error) { |
2566 | error = nl_lookup_genl_mcgroup(OVS_VPORT_FAMILY, OVS_VPORT_MCGROUP, | |
b3dcb73c | 2567 | &ovs_vport_mcgroup); |
c7178a0b | 2568 | } |
eb8ed438 | 2569 | |
921c370a EG |
2570 | ovs_tunnels_out_of_tree = dpif_netlink_rtnl_probe_oot_tunnels(); |
2571 | ||
eb8ed438 | 2572 | ovsthread_once_done(&once); |
982b8810 BP |
2573 | } |
2574 | ||
2575 | return error; | |
96fba48f BP |
2576 | } |
2577 | ||
c19e6535 | 2578 | bool |
93451a0a | 2579 | dpif_netlink_is_internal_device(const char *name) |
9fe3b9a2 | 2580 | { |
93451a0a | 2581 | struct dpif_netlink_vport reply; |
c19e6535 | 2582 | struct ofpbuf *buf; |
9fe3b9a2 | 2583 | int error; |
96fba48f | 2584 | |
93451a0a | 2585 | error = dpif_netlink_vport_get(name, &reply, &buf); |
c19e6535 BP |
2586 | if (!error) { |
2587 | ofpbuf_delete(buf); | |
141d9ce4 | 2588 | } else if (error != ENODEV && error != ENOENT) { |
c19e6535 | 2589 | VLOG_WARN_RL(&error_rl, "%s: vport query failed (%s)", |
10a89ef0 | 2590 | name, ovs_strerror(error)); |
96fba48f BP |
2591 | } |
2592 | ||
df2c07f4 | 2593 | return reply.type == OVS_VPORT_TYPE_INTERNAL; |
96fba48f | 2594 | } |
e0467f6d | 2595 | |
df2c07f4 | 2596 | /* Parses the contents of 'buf', which contains a "struct ovs_header" followed |
c19e6535 BP |
2597 | * by Netlink attributes, into 'vport'. Returns 0 if successful, otherwise a |
2598 | * positive errno value. | |
2599 | * | |
2600 | * 'vport' will contain pointers into 'buf', so the caller should not free | |
2601 | * 'buf' while 'vport' is still in use. */ | |
2602 | static int | |
93451a0a | 2603 | dpif_netlink_vport_from_ofpbuf(struct dpif_netlink_vport *vport, |
c19e6535 BP |
2604 | const struct ofpbuf *buf) |
2605 | { | |
df2c07f4 JP |
2606 | static const struct nl_policy ovs_vport_policy[] = { |
2607 | [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32 }, | |
2608 | [OVS_VPORT_ATTR_TYPE] = { .type = NL_A_U32 }, | |
2609 | [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .max_len = IFNAMSIZ }, | |
1579cf67 | 2610 | [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NL_A_UNSPEC }, |
f7df9823 | 2611 | [OVS_VPORT_ATTR_STATS] = { NL_POLICY_FOR(struct ovs_vport_stats), |
c19e6535 | 2612 | .optional = true }, |
df2c07f4 | 2613 | [OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = true }, |
c19e6535 BP |
2614 | }; |
2615 | ||
93451a0a | 2616 | dpif_netlink_vport_init(vport); |
c19e6535 | 2617 | |
0a2869d5 BP |
2618 | struct ofpbuf b = ofpbuf_const_initializer(buf->data, buf->size); |
2619 | struct nlmsghdr *nlmsg = ofpbuf_try_pull(&b, sizeof *nlmsg); | |
2620 | struct genlmsghdr *genl = ofpbuf_try_pull(&b, sizeof *genl); | |
2621 | struct ovs_header *ovs_header = ofpbuf_try_pull(&b, sizeof *ovs_header); | |
2622 | ||
2623 | struct nlattr *a[ARRAY_SIZE(ovs_vport_policy)]; | |
df2c07f4 JP |
2624 | if (!nlmsg || !genl || !ovs_header |
2625 | || nlmsg->nlmsg_type != ovs_vport_family | |
2626 | || !nl_policy_parse(&b, 0, ovs_vport_policy, a, | |
2627 | ARRAY_SIZE(ovs_vport_policy))) { | |
c19e6535 BP |
2628 | return EINVAL; |
2629 | } | |
c19e6535 | 2630 | |
f0fef760 | 2631 | vport->cmd = genl->cmd; |
df2c07f4 | 2632 | vport->dp_ifindex = ovs_header->dp_ifindex; |
4e022ec0 | 2633 | vport->port_no = nl_attr_get_odp_port(a[OVS_VPORT_ATTR_PORT_NO]); |
df2c07f4 JP |
2634 | vport->type = nl_attr_get_u32(a[OVS_VPORT_ATTR_TYPE]); |
2635 | vport->name = nl_attr_get_string(a[OVS_VPORT_ATTR_NAME]); | |
b063d9f0 | 2636 | if (a[OVS_VPORT_ATTR_UPCALL_PID]) { |
1579cf67 AW |
2637 | vport->n_upcall_pids = nl_attr_get_size(a[OVS_VPORT_ATTR_UPCALL_PID]) |
2638 | / (sizeof *vport->upcall_pids); | |
2639 | vport->upcall_pids = nl_attr_get(a[OVS_VPORT_ATTR_UPCALL_PID]); | |
2640 | ||
b063d9f0 | 2641 | } |
df2c07f4 JP |
2642 | if (a[OVS_VPORT_ATTR_STATS]) { |
2643 | vport->stats = nl_attr_get(a[OVS_VPORT_ATTR_STATS]); | |
2644 | } | |
df2c07f4 JP |
2645 | if (a[OVS_VPORT_ATTR_OPTIONS]) { |
2646 | vport->options = nl_attr_get(a[OVS_VPORT_ATTR_OPTIONS]); | |
2647 | vport->options_len = nl_attr_get_size(a[OVS_VPORT_ATTR_OPTIONS]); | |
c19e6535 | 2648 | } |
c19e6535 BP |
2649 | return 0; |
2650 | } | |
2651 | ||
df2c07f4 | 2652 | /* Appends to 'buf' (which must initially be empty) a "struct ovs_header" |
c19e6535 BP |
2653 | * followed by Netlink attributes corresponding to 'vport'. */ |
2654 | static void | |
93451a0a AS |
2655 | dpif_netlink_vport_to_ofpbuf(const struct dpif_netlink_vport *vport, |
2656 | struct ofpbuf *buf) | |
c19e6535 | 2657 | { |
df2c07f4 | 2658 | struct ovs_header *ovs_header; |
f0fef760 | 2659 | |
df2c07f4 | 2660 | nl_msg_put_genlmsghdr(buf, 0, ovs_vport_family, NLM_F_REQUEST | NLM_F_ECHO, |
69685a88 | 2661 | vport->cmd, OVS_VPORT_VERSION); |
c19e6535 | 2662 | |
df2c07f4 JP |
2663 | ovs_header = ofpbuf_put_uninit(buf, sizeof *ovs_header); |
2664 | ovs_header->dp_ifindex = vport->dp_ifindex; | |
c19e6535 | 2665 | |
4e022ec0 AW |
2666 | if (vport->port_no != ODPP_NONE) { |
2667 | nl_msg_put_odp_port(buf, OVS_VPORT_ATTR_PORT_NO, vport->port_no); | |
c19e6535 BP |
2668 | } |
2669 | ||
df2c07f4 JP |
2670 | if (vport->type != OVS_VPORT_TYPE_UNSPEC) { |
2671 | nl_msg_put_u32(buf, OVS_VPORT_ATTR_TYPE, vport->type); | |
c19e6535 BP |
2672 | } |
2673 | ||
2674 | if (vport->name) { | |
df2c07f4 | 2675 | nl_msg_put_string(buf, OVS_VPORT_ATTR_NAME, vport->name); |
c19e6535 BP |
2676 | } |
2677 | ||
1579cf67 AW |
2678 | if (vport->upcall_pids) { |
2679 | nl_msg_put_unspec(buf, OVS_VPORT_ATTR_UPCALL_PID, | |
2680 | vport->upcall_pids, | |
2681 | vport->n_upcall_pids * sizeof *vport->upcall_pids); | |
a24a6574 | 2682 | } |
b063d9f0 | 2683 | |
c19e6535 | 2684 | if (vport->stats) { |
df2c07f4 | 2685 | nl_msg_put_unspec(buf, OVS_VPORT_ATTR_STATS, |
c19e6535 BP |
2686 | vport->stats, sizeof *vport->stats); |
2687 | } | |
2688 | ||
c19e6535 | 2689 | if (vport->options) { |
df2c07f4 | 2690 | nl_msg_put_nested(buf, OVS_VPORT_ATTR_OPTIONS, |
c19e6535 BP |
2691 | vport->options, vport->options_len); |
2692 | } | |
c19e6535 BP |
2693 | } |
2694 | ||
2695 | /* Clears 'vport' to "empty" values. */ | |
2696 | void | |
93451a0a | 2697 | dpif_netlink_vport_init(struct dpif_netlink_vport *vport) |
c19e6535 BP |
2698 | { |
2699 | memset(vport, 0, sizeof *vport); | |
4e022ec0 | 2700 | vport->port_no = ODPP_NONE; |
c19e6535 BP |
2701 | } |
2702 | ||
2703 | /* Executes 'request' in the kernel datapath. If the command fails, returns a | |
2704 | * positive errno value. Otherwise, if 'reply' and 'bufp' are null, returns 0 | |
2705 | * without doing anything else. If 'reply' and 'bufp' are nonnull, then the | |
df2c07f4 | 2706 | * result of the command is expected to be an ovs_vport also, which is decoded |
c19e6535 BP |
2707 | * and stored in '*reply' and '*bufp'. The caller must free '*bufp' when the |
2708 | * reply is no longer needed ('reply' will contain pointers into '*bufp'). */ | |
2709 | int | |
93451a0a AS |
2710 | dpif_netlink_vport_transact(const struct dpif_netlink_vport *request, |
2711 | struct dpif_netlink_vport *reply, | |
2712 | struct ofpbuf **bufp) | |
c19e6535 | 2713 | { |
f0fef760 | 2714 | struct ofpbuf *request_buf; |
c19e6535 BP |
2715 | int error; |
2716 | ||
cb22974d | 2717 | ovs_assert((reply != NULL) == (bufp != NULL)); |
c19e6535 | 2718 | |
93451a0a | 2719 | error = dpif_netlink_init(); |
42bb6c72 BP |
2720 | if (error) { |
2721 | if (reply) { | |
2722 | *bufp = NULL; | |
93451a0a | 2723 | dpif_netlink_vport_init(reply); |
42bb6c72 BP |
2724 | } |
2725 | return error; | |
2726 | } | |
2727 | ||
f0fef760 | 2728 | request_buf = ofpbuf_new(1024); |
93451a0a | 2729 | dpif_netlink_vport_to_ofpbuf(request, request_buf); |
a88b4e04 | 2730 | error = nl_transact(NETLINK_GENERIC, request_buf, bufp); |
f0fef760 | 2731 | ofpbuf_delete(request_buf); |
c19e6535 | 2732 | |
f0fef760 BP |
2733 | if (reply) { |
2734 | if (!error) { | |
93451a0a | 2735 | error = dpif_netlink_vport_from_ofpbuf(reply, *bufp); |
f0fef760 | 2736 | } |
c19e6535 | 2737 | if (error) { |
93451a0a | 2738 | dpif_netlink_vport_init(reply); |
f0fef760 BP |
2739 | ofpbuf_delete(*bufp); |
2740 | *bufp = NULL; | |
c19e6535 | 2741 | } |
c19e6535 BP |
2742 | } |
2743 | return error; | |
2744 | } | |
2745 | ||
2746 | /* Obtains information about the kernel vport named 'name' and stores it into | |
2747 | * '*reply' and '*bufp'. The caller must free '*bufp' when the reply is no | |
2748 | * longer needed ('reply' will contain pointers into '*bufp'). */ | |
2749 | int | |
93451a0a AS |
2750 | dpif_netlink_vport_get(const char *name, struct dpif_netlink_vport *reply, |
2751 | struct ofpbuf **bufp) | |
c19e6535 | 2752 | { |
93451a0a | 2753 | struct dpif_netlink_vport request; |
c19e6535 | 2754 | |
93451a0a | 2755 | dpif_netlink_vport_init(&request); |
df2c07f4 | 2756 | request.cmd = OVS_VPORT_CMD_GET; |
c19e6535 BP |
2757 | request.name = name; |
2758 | ||
93451a0a | 2759 | return dpif_netlink_vport_transact(&request, reply, bufp); |
c19e6535 | 2760 | } |
93451a0a | 2761 | |
df2c07f4 | 2762 | /* Parses the contents of 'buf', which contains a "struct ovs_header" followed |
aaff4b55 BP |
2763 | * by Netlink attributes, into 'dp'. Returns 0 if successful, otherwise a |
2764 | * positive errno value. | |
d6569377 BP |
2765 | * |
2766 | * 'dp' will contain pointers into 'buf', so the caller should not free 'buf' | |
2767 | * while 'dp' is still in use. */ | |
2768 | static int | |
93451a0a | 2769 | dpif_netlink_dp_from_ofpbuf(struct dpif_netlink_dp *dp, const struct ofpbuf *buf) |
d6569377 | 2770 | { |
df2c07f4 JP |
2771 | static const struct nl_policy ovs_datapath_policy[] = { |
2772 | [OVS_DP_ATTR_NAME] = { .type = NL_A_STRING, .max_len = IFNAMSIZ }, | |
f7df9823 | 2773 | [OVS_DP_ATTR_STATS] = { NL_POLICY_FOR(struct ovs_dp_stats), |
d6569377 | 2774 | .optional = true }, |
847108dc AZ |
2775 | [OVS_DP_ATTR_MEGAFLOW_STATS] = { |
2776 | NL_POLICY_FOR(struct ovs_dp_megaflow_stats), | |
2777 | .optional = true }, | |
d6569377 BP |
2778 | }; |
2779 | ||
93451a0a | 2780 | dpif_netlink_dp_init(dp); |
d6569377 | 2781 | |
0a2869d5 BP |
2782 | struct ofpbuf b = ofpbuf_const_initializer(buf->data, buf->size); |
2783 | struct nlmsghdr *nlmsg = ofpbuf_try_pull(&b, sizeof *nlmsg); | |
2784 | struct genlmsghdr *genl = ofpbuf_try_pull(&b, sizeof *genl); | |
2785 | struct ovs_header *ovs_header = ofpbuf_try_pull(&b, sizeof *ovs_header); | |
2786 | ||
2787 | struct nlattr *a[ARRAY_SIZE(ovs_datapath_policy)]; | |
df2c07f4 JP |
2788 | if (!nlmsg || !genl || !ovs_header |
2789 | || nlmsg->nlmsg_type != ovs_datapath_family | |
2790 | || !nl_policy_parse(&b, 0, ovs_datapath_policy, a, | |
2791 | ARRAY_SIZE(ovs_datapath_policy))) { | |
d6569377 BP |
2792 | return EINVAL; |
2793 | } | |
d6569377 | 2794 | |
aaff4b55 | 2795 | dp->cmd = genl->cmd; |
df2c07f4 JP |
2796 | dp->dp_ifindex = ovs_header->dp_ifindex; |
2797 | dp->name = nl_attr_get_string(a[OVS_DP_ATTR_NAME]); | |
2798 | if (a[OVS_DP_ATTR_STATS]) { | |
6a54dedc | 2799 | dp->stats = nl_attr_get(a[OVS_DP_ATTR_STATS]); |
d6569377 | 2800 | } |
982b8810 | 2801 | |
847108dc | 2802 | if (a[OVS_DP_ATTR_MEGAFLOW_STATS]) { |
6a54dedc | 2803 | dp->megaflow_stats = nl_attr_get(a[OVS_DP_ATTR_MEGAFLOW_STATS]); |
847108dc AZ |
2804 | } |
2805 | ||
d6569377 BP |
2806 | return 0; |
2807 | } | |
2808 | ||
aaff4b55 | 2809 | /* Appends to 'buf' the Generic Netlink message described by 'dp'. */ |
d6569377 | 2810 | static void |
93451a0a | 2811 | dpif_netlink_dp_to_ofpbuf(const struct dpif_netlink_dp *dp, struct ofpbuf *buf) |
d6569377 | 2812 | { |
df2c07f4 | 2813 | struct ovs_header *ovs_header; |
d6569377 | 2814 | |
df2c07f4 | 2815 | nl_msg_put_genlmsghdr(buf, 0, ovs_datapath_family, |
69685a88 JG |
2816 | NLM_F_REQUEST | NLM_F_ECHO, dp->cmd, |
2817 | OVS_DATAPATH_VERSION); | |
aaff4b55 | 2818 | |
df2c07f4 JP |
2819 | ovs_header = ofpbuf_put_uninit(buf, sizeof *ovs_header); |
2820 | ovs_header->dp_ifindex = dp->dp_ifindex; | |
d6569377 BP |
2821 | |
2822 | if (dp->name) { | |
df2c07f4 | 2823 | nl_msg_put_string(buf, OVS_DP_ATTR_NAME, dp->name); |
d6569377 BP |
2824 | } |
2825 | ||
a24a6574 BP |
2826 | if (dp->upcall_pid) { |
2827 | nl_msg_put_u32(buf, OVS_DP_ATTR_UPCALL_PID, *dp->upcall_pid); | |
2828 | } | |
b063d9f0 | 2829 | |
b7fd5e38 TG |
2830 | if (dp->user_features) { |
2831 | nl_msg_put_u32(buf, OVS_DP_ATTR_USER_FEATURES, dp->user_features); | |
2832 | } | |
2833 | ||
df2c07f4 | 2834 | /* Skip OVS_DP_ATTR_STATS since we never have a reason to serialize it. */ |
d6569377 BP |
2835 | } |
2836 | ||
2837 | /* Clears 'dp' to "empty" values. */ | |
d3d8f1f7 | 2838 | static void |
93451a0a | 2839 | dpif_netlink_dp_init(struct dpif_netlink_dp *dp) |
d6569377 BP |
2840 | { |
2841 | memset(dp, 0, sizeof *dp); | |
d6569377 BP |
2842 | } |
2843 | ||
aaff4b55 | 2844 | static void |
93451a0a | 2845 | dpif_netlink_dp_dump_start(struct nl_dump *dump) |
aaff4b55 | 2846 | { |
93451a0a | 2847 | struct dpif_netlink_dp request; |
aaff4b55 BP |
2848 | struct ofpbuf *buf; |
2849 | ||
93451a0a | 2850 | dpif_netlink_dp_init(&request); |
df2c07f4 | 2851 | request.cmd = OVS_DP_CMD_GET; |
aaff4b55 BP |
2852 | |
2853 | buf = ofpbuf_new(1024); | |
93451a0a | 2854 | dpif_netlink_dp_to_ofpbuf(&request, buf); |
a88b4e04 | 2855 | nl_dump_start(dump, NETLINK_GENERIC, buf); |
aaff4b55 BP |
2856 | ofpbuf_delete(buf); |
2857 | } | |
2858 | ||
d6569377 BP |
2859 | /* Executes 'request' in the kernel datapath. If the command fails, returns a |
2860 | * positive errno value. Otherwise, if 'reply' and 'bufp' are null, returns 0 | |
2861 | * without doing anything else. If 'reply' and 'bufp' are nonnull, then the | |
aaff4b55 BP |
2862 | * result of the command is expected to be of the same form, which is decoded |
2863 | * and stored in '*reply' and '*bufp'. The caller must free '*bufp' when the | |
2864 | * reply is no longer needed ('reply' will contain pointers into '*bufp'). */ | |
d3d8f1f7 | 2865 | static int |
93451a0a AS |
2866 | dpif_netlink_dp_transact(const struct dpif_netlink_dp *request, |
2867 | struct dpif_netlink_dp *reply, struct ofpbuf **bufp) | |
d6569377 | 2868 | { |
aaff4b55 | 2869 | struct ofpbuf *request_buf; |
d6569377 | 2870 | int error; |
d6569377 | 2871 | |
cb22974d | 2872 | ovs_assert((reply != NULL) == (bufp != NULL)); |
d6569377 | 2873 | |
aaff4b55 | 2874 | request_buf = ofpbuf_new(1024); |
93451a0a | 2875 | dpif_netlink_dp_to_ofpbuf(request, request_buf); |
a88b4e04 | 2876 | error = nl_transact(NETLINK_GENERIC, request_buf, bufp); |
aaff4b55 | 2877 | ofpbuf_delete(request_buf); |
d6569377 | 2878 | |
aaff4b55 | 2879 | if (reply) { |
93451a0a | 2880 | dpif_netlink_dp_init(reply); |
aaff4b55 | 2881 | if (!error) { |
93451a0a | 2882 | error = dpif_netlink_dp_from_ofpbuf(reply, *bufp); |
aaff4b55 | 2883 | } |
d6569377 | 2884 | if (error) { |
aaff4b55 BP |
2885 | ofpbuf_delete(*bufp); |
2886 | *bufp = NULL; | |
d6569377 | 2887 | } |
d6569377 BP |
2888 | } |
2889 | return error; | |
2890 | } | |
2891 | ||
2892 | /* Obtains information about 'dpif_' and stores it into '*reply' and '*bufp'. | |
2893 | * The caller must free '*bufp' when the reply is no longer needed ('reply' | |
2894 | * will contain pointers into '*bufp'). */ | |
d3d8f1f7 | 2895 | static int |
93451a0a AS |
2896 | dpif_netlink_dp_get(const struct dpif *dpif_, struct dpif_netlink_dp *reply, |
2897 | struct ofpbuf **bufp) | |
d6569377 | 2898 | { |
93451a0a AS |
2899 | struct dpif_netlink *dpif = dpif_netlink_cast(dpif_); |
2900 | struct dpif_netlink_dp request; | |
d6569377 | 2901 | |
93451a0a | 2902 | dpif_netlink_dp_init(&request); |
df2c07f4 | 2903 | request.cmd = OVS_DP_CMD_GET; |
254f2dc8 | 2904 | request.dp_ifindex = dpif->dp_ifindex; |
d6569377 | 2905 | |
93451a0a | 2906 | return dpif_netlink_dp_transact(&request, reply, bufp); |
d6569377 | 2907 | } |
93451a0a | 2908 | |
df2c07f4 | 2909 | /* Parses the contents of 'buf', which contains a "struct ovs_header" followed |
37a1300c | 2910 | * by Netlink attributes, into 'flow'. Returns 0 if successful, otherwise a |
d6569377 BP |
2911 | * positive errno value. |
2912 | * | |
2913 | * 'flow' will contain pointers into 'buf', so the caller should not free 'buf' | |
2914 | * while 'flow' is still in use. */ | |
2915 | static int | |
93451a0a AS |
2916 | dpif_netlink_flow_from_ofpbuf(struct dpif_netlink_flow *flow, |
2917 | const struct ofpbuf *buf) | |
d6569377 | 2918 | { |
70e5ed6f JS |
2919 | static const struct nl_policy ovs_flow_policy[__OVS_FLOW_ATTR_MAX] = { |
2920 | [OVS_FLOW_ATTR_KEY] = { .type = NL_A_NESTED, .optional = true }, | |
e6cc0bab | 2921 | [OVS_FLOW_ATTR_MASK] = { .type = NL_A_NESTED, .optional = true }, |
df2c07f4 | 2922 | [OVS_FLOW_ATTR_ACTIONS] = { .type = NL_A_NESTED, .optional = true }, |
f7df9823 | 2923 | [OVS_FLOW_ATTR_STATS] = { NL_POLICY_FOR(struct ovs_flow_stats), |
d6569377 | 2924 | .optional = true }, |
df2c07f4 JP |
2925 | [OVS_FLOW_ATTR_TCP_FLAGS] = { .type = NL_A_U8, .optional = true }, |
2926 | [OVS_FLOW_ATTR_USED] = { .type = NL_A_U64, .optional = true }, | |
ab79d262 | 2927 | [OVS_FLOW_ATTR_UFID] = { .type = NL_A_U128, .optional = true }, |
df2c07f4 | 2928 | /* The kernel never uses OVS_FLOW_ATTR_CLEAR. */ |
43f9ac0a | 2929 | /* The kernel never uses OVS_FLOW_ATTR_PROBE. */ |
70e5ed6f | 2930 | /* The kernel never uses OVS_FLOW_ATTR_UFID_FLAGS. */ |
d6569377 BP |
2931 | }; |
2932 | ||
93451a0a | 2933 | dpif_netlink_flow_init(flow); |
d6569377 | 2934 | |
0a2869d5 BP |
2935 | struct ofpbuf b = ofpbuf_const_initializer(buf->data, buf->size); |
2936 | struct nlmsghdr *nlmsg = ofpbuf_try_pull(&b, sizeof *nlmsg); | |
2937 | struct genlmsghdr *genl = ofpbuf_try_pull(&b, sizeof *genl); | |
2938 | struct ovs_header *ovs_header = ofpbuf_try_pull(&b, sizeof *ovs_header); | |
2939 | ||
2940 | struct nlattr *a[ARRAY_SIZE(ovs_flow_policy)]; | |
df2c07f4 JP |
2941 | if (!nlmsg || !genl || !ovs_header |
2942 | || nlmsg->nlmsg_type != ovs_flow_family | |
2943 | || !nl_policy_parse(&b, 0, ovs_flow_policy, a, | |
2944 | ARRAY_SIZE(ovs_flow_policy))) { | |
d6569377 BP |
2945 | return EINVAL; |
2946 | } | |
70e5ed6f JS |
2947 | if (!a[OVS_FLOW_ATTR_KEY] && !a[OVS_FLOW_ATTR_UFID]) { |
2948 | return EINVAL; | |
2949 | } | |
d6569377 | 2950 | |
37a1300c | 2951 | flow->nlmsg_flags = nlmsg->nlmsg_flags; |
df2c07f4 | 2952 | flow->dp_ifindex = ovs_header->dp_ifindex; |
70e5ed6f JS |
2953 | if (a[OVS_FLOW_ATTR_KEY]) { |
2954 | flow->key = nl_attr_get(a[OVS_FLOW_ATTR_KEY]); | |
2955 | flow->key_len = nl_attr_get_size(a[OVS_FLOW_ATTR_KEY]); | |
2956 | } | |
e6cc0bab | 2957 | |
70e5ed6f | 2958 | if (a[OVS_FLOW_ATTR_UFID]) { |
ab79d262 | 2959 | flow->ufid = nl_attr_get_u128(a[OVS_FLOW_ATTR_UFID]); |
70e5ed6f JS |
2960 | flow->ufid_present = true; |
2961 | } | |
e6cc0bab AZ |
2962 | if (a[OVS_FLOW_ATTR_MASK]) { |
2963 | flow->mask = nl_attr_get(a[OVS_FLOW_ATTR_MASK]); | |
2964 | flow->mask_len = nl_attr_get_size(a[OVS_FLOW_ATTR_MASK]); | |
2965 | } | |
df2c07f4 JP |
2966 | if (a[OVS_FLOW_ATTR_ACTIONS]) { |
2967 | flow->actions = nl_attr_get(a[OVS_FLOW_ATTR_ACTIONS]); | |
2968 | flow->actions_len = nl_attr_get_size(a[OVS_FLOW_ATTR_ACTIONS]); | |
d6569377 | 2969 | } |
df2c07f4 JP |
2970 | if (a[OVS_FLOW_ATTR_STATS]) { |
2971 | flow->stats = nl_attr_get(a[OVS_FLOW_ATTR_STATS]); | |
d6569377 | 2972 | } |
df2c07f4 JP |
2973 | if (a[OVS_FLOW_ATTR_TCP_FLAGS]) { |
2974 | flow->tcp_flags = nl_attr_get(a[OVS_FLOW_ATTR_TCP_FLAGS]); | |
d6569377 | 2975 | } |
df2c07f4 JP |
2976 | if (a[OVS_FLOW_ATTR_USED]) { |
2977 | flow->used = nl_attr_get(a[OVS_FLOW_ATTR_USED]); | |
9e980142 | 2978 | } |
d6569377 BP |
2979 | return 0; |
2980 | } | |
2981 | ||
beb75a40 JS |
2982 | |
2983 | /* | |
2984 | * If PACKET_TYPE attribute is present in 'data', it filters PACKET_TYPE out, | |
2985 | * then puts 'data' to 'buf'. | |
2986 | */ | |
2987 | static void | |
2988 | put_exclude_packet_type(struct ofpbuf *buf, uint16_t type, | |
2989 | const struct nlattr *data, uint16_t data_len) | |
2990 | { | |
2991 | const struct nlattr *packet_type; | |
2992 | ||
2993 | packet_type = nl_attr_find__(data, data_len, OVS_KEY_ATTR_PACKET_TYPE); | |
2994 | ||
2995 | if (packet_type) { | |
2996 | /* exclude PACKET_TYPE Netlink attribute. */ | |
2997 | ovs_assert(NLA_ALIGN(packet_type->nla_len) == NL_A_U32_SIZE); | |
2998 | size_t packet_type_len = NL_A_U32_SIZE; | |
2999 | size_t first_chunk_size = (uint8_t *)packet_type - (uint8_t *)data; | |
3000 | size_t second_chunk_size = data_len - first_chunk_size | |
3001 | - packet_type_len; | |
3002 | uint8_t *first_attr = NULL; | |
3003 | struct nlattr *next_attr = nl_attr_next(packet_type); | |
3004 | ||
3005 | first_attr = nl_msg_put_unspec_uninit(buf, type, | |
3006 | data_len - packet_type_len); | |
3007 | memcpy(first_attr, data, first_chunk_size); | |
3008 | memcpy(first_attr + first_chunk_size, next_attr, second_chunk_size); | |
3009 | } else { | |
3010 | nl_msg_put_unspec(buf, type, data, data_len); | |
3011 | } | |
3012 | } | |
3013 | ||
df2c07f4 | 3014 | /* Appends to 'buf' (which must initially be empty) a "struct ovs_header" |
d6569377 BP |
3015 | * followed by Netlink attributes corresponding to 'flow'. */ |
3016 | static void | |
93451a0a AS |
3017 | dpif_netlink_flow_to_ofpbuf(const struct dpif_netlink_flow *flow, |
3018 | struct ofpbuf *buf) | |
d6569377 | 3019 | { |
df2c07f4 | 3020 | struct ovs_header *ovs_header; |
d6569377 | 3021 | |
df2c07f4 | 3022 | nl_msg_put_genlmsghdr(buf, 0, ovs_flow_family, |
30b44744 | 3023 | NLM_F_REQUEST | flow->nlmsg_flags, |
69685a88 | 3024 | flow->cmd, OVS_FLOW_VERSION); |
37a1300c | 3025 | |
df2c07f4 JP |
3026 | ovs_header = ofpbuf_put_uninit(buf, sizeof *ovs_header); |
3027 | ovs_header->dp_ifindex = flow->dp_ifindex; | |
d6569377 | 3028 | |
70e5ed6f | 3029 | if (flow->ufid_present) { |
ab79d262 | 3030 | nl_msg_put_u128(buf, OVS_FLOW_ATTR_UFID, flow->ufid); |
70e5ed6f JS |
3031 | } |
3032 | if (flow->ufid_terse) { | |
3033 | nl_msg_put_u32(buf, OVS_FLOW_ATTR_UFID_FLAGS, | |
3034 | OVS_UFID_F_OMIT_KEY | OVS_UFID_F_OMIT_MASK | |
3035 | | OVS_UFID_F_OMIT_ACTIONS); | |
3036 | } | |
64bb477f JS |
3037 | if (!flow->ufid_terse || !flow->ufid_present) { |
3038 | if (flow->key_len) { | |
beb75a40 JS |
3039 | put_exclude_packet_type(buf, OVS_FLOW_ATTR_KEY, flow->key, |
3040 | flow->key_len); | |
64bb477f | 3041 | } |
64bb477f | 3042 | if (flow->mask_len) { |
beb75a40 JS |
3043 | put_exclude_packet_type(buf, OVS_FLOW_ATTR_MASK, flow->mask, |
3044 | flow->mask_len); | |
64bb477f JS |
3045 | } |
3046 | if (flow->actions || flow->actions_len) { | |
3047 | nl_msg_put_unspec(buf, OVS_FLOW_ATTR_ACTIONS, | |
3048 | flow->actions, flow->actions_len); | |
3049 | } | |
d6569377 BP |
3050 | } |
3051 | ||
3052 | /* We never need to send these to the kernel. */ | |
cb22974d BP |
3053 | ovs_assert(!flow->stats); |
3054 | ovs_assert(!flow->tcp_flags); | |
3055 | ovs_assert(!flow->used); | |
d6569377 BP |
3056 | |
3057 | if (flow->clear) { | |
df2c07f4 | 3058 | nl_msg_put_flag(buf, OVS_FLOW_ATTR_CLEAR); |
d6569377 | 3059 | } |
43f9ac0a JR |
3060 | if (flow->probe) { |
3061 | nl_msg_put_flag(buf, OVS_FLOW_ATTR_PROBE); | |
3062 | } | |
d6569377 BP |
3063 | } |
3064 | ||
3065 | /* Clears 'flow' to "empty" values. */ | |
d3d8f1f7 | 3066 | static void |
93451a0a | 3067 | dpif_netlink_flow_init(struct dpif_netlink_flow *flow) |
d6569377 BP |
3068 | { |
3069 | memset(flow, 0, sizeof *flow); | |
3070 | } | |
3071 | ||
3072 | /* Executes 'request' in the kernel datapath. If the command fails, returns a | |
3073 | * positive errno value. Otherwise, if 'reply' and 'bufp' are null, returns 0 | |
3074 | * without doing anything else. If 'reply' and 'bufp' are nonnull, then the | |
37a1300c BP |
3075 | * result of the command is expected to be a flow also, which is decoded and |
3076 | * stored in '*reply' and '*bufp'. The caller must free '*bufp' when the reply | |
3077 | * is no longer needed ('reply' will contain pointers into '*bufp'). */ | |
d3d8f1f7 | 3078 | static int |
93451a0a AS |
3079 | dpif_netlink_flow_transact(struct dpif_netlink_flow *request, |
3080 | struct dpif_netlink_flow *reply, | |
3081 | struct ofpbuf **bufp) | |
d6569377 | 3082 | { |
37a1300c | 3083 | struct ofpbuf *request_buf; |
d6569377 | 3084 | int error; |
d6569377 | 3085 | |
cb22974d | 3086 | ovs_assert((reply != NULL) == (bufp != NULL)); |
d6569377 | 3087 | |
30b44744 BP |
3088 | if (reply) { |
3089 | request->nlmsg_flags |= NLM_F_ECHO; | |
3090 | } | |
3091 | ||
37a1300c | 3092 | request_buf = ofpbuf_new(1024); |
93451a0a | 3093 | dpif_netlink_flow_to_ofpbuf(request, request_buf); |
a88b4e04 | 3094 | error = nl_transact(NETLINK_GENERIC, request_buf, bufp); |
37a1300c | 3095 | ofpbuf_delete(request_buf); |
d6569377 | 3096 | |
37a1300c BP |
3097 | if (reply) { |
3098 | if (!error) { | |
93451a0a | 3099 | error = dpif_netlink_flow_from_ofpbuf(reply, *bufp); |
37a1300c | 3100 | } |
d6569377 | 3101 | if (error) { |
93451a0a | 3102 | dpif_netlink_flow_init(reply); |
37a1300c BP |
3103 | ofpbuf_delete(*bufp); |
3104 | *bufp = NULL; | |
d6569377 | 3105 | } |
d6569377 BP |
3106 | } |
3107 | return error; | |
3108 | } | |
3109 | ||
3110 | static void | |
93451a0a AS |
3111 | dpif_netlink_flow_get_stats(const struct dpif_netlink_flow *flow, |
3112 | struct dpif_flow_stats *stats) | |
d6569377 BP |
3113 | { |
3114 | if (flow->stats) { | |
6a54dedc BP |
3115 | stats->n_packets = get_32aligned_u64(&flow->stats->n_packets); |
3116 | stats->n_bytes = get_32aligned_u64(&flow->stats->n_bytes); | |
d6569377 BP |
3117 | } else { |
3118 | stats->n_packets = 0; | |
3119 | stats->n_bytes = 0; | |
3120 | } | |
0e70cdcb | 3121 | stats->used = flow->used ? get_32aligned_u64(flow->used) : 0; |
d6569377 BP |
3122 | stats->tcp_flags = flow->tcp_flags ? *flow->tcp_flags : 0; |
3123 | } | |
e0467f6d | 3124 | |
14b4d2f9 BP |
3125 | /* Logs information about a packet that was recently lost in 'ch' (in |
3126 | * 'dpif_'). */ | |
3127 | static void | |
93451a0a | 3128 | report_loss(struct dpif_netlink *dpif, struct dpif_channel *ch, uint32_t ch_idx, |
1579cf67 | 3129 | uint32_t handler_id) |
14b4d2f9 | 3130 | { |
14b4d2f9 | 3131 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5); |
14b4d2f9 BP |
3132 | struct ds s; |
3133 | ||
8d675c5a | 3134 | if (VLOG_DROP_WARN(&rl)) { |
14b4d2f9 BP |
3135 | return; |
3136 | } | |
3137 | ||
3138 | ds_init(&s); | |
3139 | if (ch->last_poll != LLONG_MIN) { | |
3140 | ds_put_format(&s, " (last polled %lld ms ago)", | |
3141 | time_msec() - ch->last_poll); | |
3142 | } | |
14b4d2f9 | 3143 | |
1579cf67 | 3144 | VLOG_WARN("%s: lost packet on port channel %u of handler %u", |
9b00386b | 3145 | dpif_name(&dpif->dpif), ch_idx, handler_id); |
14b4d2f9 BP |
3146 | ds_destroy(&s); |
3147 | } |