]>
Commit | Line | Data |
---|---|---|
f9c83246 | 1 | /* Copyright (c) 2015, 2016 Red Hat, Inc. |
04f48a68 | 2 | * Copyright (c) 2017 Nicira, Inc. |
27732ac4 BS |
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> | |
6335d074 | 18 | |
27732ac4 | 19 | #include "pinctrl.h" |
6335d074 | 20 | |
0bac7164 | 21 | #include "coverage.h" |
42814145 | 22 | #include "csum.h" |
6335d074 BP |
23 | #include "dirs.h" |
24 | #include "dp-packet.h" | |
f4248336 | 25 | #include "flow.h" |
0bac7164 | 26 | #include "lport.h" |
42814145 | 27 | #include "nx-match.h" |
0ee8aaf6 | 28 | #include "ovn-controller.h" |
e75451fe | 29 | #include "lib/packets.h" |
0ee8aaf6 | 30 | #include "lib/sset.h" |
b598f214 | 31 | #include "openvswitch/ofp-actions.h" |
d271907f | 32 | #include "openvswitch/ofp-msgs.h" |
25d436fb | 33 | #include "openvswitch/ofp-print.h" |
f4248336 | 34 | #include "openvswitch/ofp-util.h" |
27732ac4 | 35 | #include "openvswitch/vlog.h" |
42814145 NS |
36 | |
37 | #include "lib/dhcp.h" | |
0bac7164 | 38 | #include "ovn-controller.h" |
8b2ed684 | 39 | #include "ovn/actions.h" |
d271907f | 40 | #include "ovn/lib/logical-fields.h" |
01cfdb2f | 41 | #include "ovn/lib/ovn-dhcp.h" |
0ee8aaf6 | 42 | #include "ovn/lib/ovn-util.h" |
0bac7164 | 43 | #include "poll-loop.h" |
d271907f | 44 | #include "rconn.h" |
27732ac4 | 45 | #include "socket-util.h" |
0bac7164 | 46 | #include "timeval.h" |
27732ac4 BS |
47 | #include "vswitch-idl.h" |
48 | ||
49 | VLOG_DEFINE_THIS_MODULE(pinctrl); | |
50 | ||
51 | /* OpenFlow connection to the switch. */ | |
52 | static struct rconn *swconn; | |
53 | ||
54 | /* Last seen sequence number for 'swconn'. When this differs from | |
55 | * rconn_get_connection_seqno(rconn), 'swconn' has reconnected. */ | |
56 | static unsigned int conn_seq_no; | |
57 | ||
c34a87b6 JP |
58 | static void pinctrl_handle_put_mac_binding(const struct flow *md, |
59 | const struct flow *headers, | |
60 | bool is_arp); | |
61 | static void init_put_mac_bindings(void); | |
62 | static void destroy_put_mac_bindings(void); | |
63 | static void run_put_mac_bindings(struct controller_ctx *, | |
64 | const struct lport_index *lports); | |
65 | static void wait_put_mac_bindings(struct controller_ctx *); | |
66 | static void flush_put_mac_bindings(void); | |
0bac7164 | 67 | |
0ee8aaf6 RR |
68 | static void init_send_garps(void); |
69 | static void destroy_send_garps(void); | |
70 | static void send_garp_wait(void); | |
71 | static void send_garp_run(const struct ovsrec_bridge *, | |
f1a8bd06 | 72 | const struct sbrec_chassis *, |
0ee8aaf6 RR |
73 | const struct lport_index *lports, |
74 | struct hmap *local_datapaths); | |
f8a8db39 JP |
75 | static void pinctrl_handle_nd_na(const struct flow *ip_flow, |
76 | const struct match *md, | |
77 | struct ofpbuf *userdata); | |
e75451fe ZKL |
78 | static void reload_metadata(struct ofpbuf *ofpacts, |
79 | const struct match *md); | |
0ee8aaf6 | 80 | |
c34a87b6 | 81 | COVERAGE_DEFINE(pinctrl_drop_put_mac_binding); |
0bac7164 | 82 | |
27732ac4 BS |
83 | void |
84 | pinctrl_init(void) | |
85 | { | |
86 | swconn = rconn_create(5, 0, DSCP_DEFAULT, 1 << OFP13_VERSION); | |
87 | conn_seq_no = 0; | |
c34a87b6 | 88 | init_put_mac_bindings(); |
0ee8aaf6 | 89 | init_send_garps(); |
27732ac4 BS |
90 | } |
91 | ||
92 | static ovs_be32 | |
93 | queue_msg(struct ofpbuf *msg) | |
94 | { | |
95 | const struct ofp_header *oh = msg->data; | |
96 | ovs_be32 xid = oh->xid; | |
97 | ||
98 | rconn_send(swconn, msg, NULL); | |
99 | return xid; | |
100 | } | |
101 | ||
6335d074 | 102 | /* Sets up 'swconn', a newly (re)connected connection to a switch. */ |
27732ac4 | 103 | static void |
6335d074 | 104 | pinctrl_setup(struct rconn *swconn) |
27732ac4 | 105 | { |
6335d074 BP |
106 | /* Fetch the switch configuration. The response later will allow us to |
107 | * change the miss_send_len to UINT16_MAX, so that we can enable | |
108 | * asynchronous messages. */ | |
109 | queue_msg(ofpraw_alloc(OFPRAW_OFPT_GET_CONFIG_REQUEST, | |
110 | rconn_get_version(swconn), 0)); | |
27732ac4 | 111 | |
6335d074 BP |
112 | /* Set a packet-in format that supports userdata. */ |
113 | queue_msg(ofputil_make_set_packet_in_format(rconn_get_version(swconn), | |
114 | NXPIF_NXT_PACKET_IN2)); | |
27732ac4 BS |
115 | } |
116 | ||
117 | static void | |
660f5a61 NS |
118 | set_switch_config(struct rconn *swconn, |
119 | const struct ofputil_switch_config *config) | |
27732ac4 | 120 | { |
660f5a61 NS |
121 | enum ofp_version version = rconn_get_version(swconn); |
122 | struct ofpbuf *request = ofputil_encode_set_config(config, version); | |
27732ac4 BS |
123 | queue_msg(request); |
124 | } | |
125 | ||
126 | static void | |
0bac7164 BP |
127 | pinctrl_handle_arp(const struct flow *ip_flow, const struct match *md, |
128 | struct ofpbuf *userdata) | |
27732ac4 | 129 | { |
6335d074 BP |
130 | /* This action only works for IP packets, and the switch should only send |
131 | * us IP packets this way, but check here just to be sure. */ | |
132 | if (ip_flow->dl_type != htons(ETH_TYPE_IP)) { | |
133 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); | |
134 | VLOG_WARN_RL(&rl, "ARP action on non-IP packet (Ethertype %"PRIx16")", | |
135 | ntohs(ip_flow->dl_type)); | |
136 | return; | |
137 | } | |
138 | ||
139 | /* Compose an ARP packet. */ | |
140 | uint64_t packet_stub[128 / 8]; | |
141 | struct dp_packet packet; | |
142 | dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub); | |
143 | compose_arp__(&packet); | |
144 | ||
145 | struct eth_header *eth = dp_packet_l2(&packet); | |
146 | eth->eth_dst = ip_flow->dl_dst; | |
147 | eth->eth_src = ip_flow->dl_src; | |
148 | ||
149 | struct arp_eth_header *arp = dp_packet_l3(&packet); | |
150 | arp->ar_op = htons(ARP_OP_REQUEST); | |
151 | arp->ar_sha = ip_flow->dl_src; | |
152 | put_16aligned_be32(&arp->ar_spa, ip_flow->nw_src); | |
153 | arp->ar_tha = eth_addr_zero; | |
154 | put_16aligned_be32(&arp->ar_tpa, ip_flow->nw_dst); | |
155 | ||
156 | if (ip_flow->vlan_tci & htons(VLAN_CFI)) { | |
157 | eth_push_vlan(&packet, htons(ETH_TYPE_VLAN_8021Q), ip_flow->vlan_tci); | |
158 | } | |
159 | ||
160 | /* Compose actions. | |
161 | * | |
0bac7164 BP |
162 | * First, copy metadata from 'md' into the packet-out via "set_field" |
163 | * actions, then add actions from 'userdata'. | |
6335d074 | 164 | */ |
0bac7164 | 165 | uint64_t ofpacts_stub[4096 / 8]; |
6335d074 BP |
166 | struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub); |
167 | enum ofp_version version = rconn_get_version(swconn); | |
168 | ||
e75451fe | 169 | reload_metadata(&ofpacts, md); |
04f48a68 | 170 | enum ofperr error = ofpacts_pull_openflow_actions(userdata, userdata->size, |
5c7c16d8 YHW |
171 | version, NULL, NULL, |
172 | &ofpacts); | |
6335d074 BP |
173 | if (error) { |
174 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); | |
175 | VLOG_WARN_RL(&rl, "failed to parse arp actions (%s)", | |
176 | ofperr_to_string(error)); | |
177 | goto exit; | |
178 | } | |
179 | ||
180 | struct ofputil_packet_out po = { | |
181 | .packet = dp_packet_data(&packet), | |
182 | .packet_len = dp_packet_size(&packet), | |
183 | .buffer_id = UINT32_MAX, | |
184 | .in_port = OFPP_CONTROLLER, | |
185 | .ofpacts = ofpacts.data, | |
186 | .ofpacts_len = ofpacts.size, | |
187 | }; | |
188 | enum ofputil_protocol proto = ofputil_protocol_from_ofp_version(version); | |
189 | queue_msg(ofputil_encode_packet_out(&po, proto)); | |
27732ac4 | 190 | |
6335d074 BP |
191 | exit: |
192 | dp_packet_uninit(&packet); | |
193 | ofpbuf_uninit(&ofpacts); | |
194 | } | |
195 | ||
42814145 NS |
196 | static void |
197 | pinctrl_handle_put_dhcp_opts( | |
198 | struct dp_packet *pkt_in, struct ofputil_packet_in *pin, | |
199 | struct ofpbuf *userdata, struct ofpbuf *continuation) | |
200 | { | |
201 | enum ofp_version version = rconn_get_version(swconn); | |
202 | enum ofputil_protocol proto = ofputil_protocol_from_ofp_version(version); | |
203 | struct dp_packet *pkt_out_ptr = NULL; | |
204 | uint32_t success = 0; | |
205 | ||
206 | /* Parse result field. */ | |
207 | const struct mf_field *f; | |
04f48a68 | 208 | enum ofperr ofperr = nx_pull_header(userdata, NULL, &f, NULL); |
42814145 NS |
209 | if (ofperr) { |
210 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); | |
211 | VLOG_WARN_RL(&rl, "bad result OXM (%s)", ofperr_to_string(ofperr)); | |
212 | goto exit; | |
213 | } | |
214 | ||
215 | /* Parse result offset and offer IP. */ | |
216 | ovs_be32 *ofsp = ofpbuf_try_pull(userdata, sizeof *ofsp); | |
217 | ovs_be32 *offer_ip = ofpbuf_try_pull(userdata, sizeof *offer_ip); | |
218 | if (!ofsp || !offer_ip) { | |
219 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); | |
220 | VLOG_WARN_RL(&rl, "offset or offer_ip not present in the userdata"); | |
221 | goto exit; | |
222 | } | |
223 | ||
224 | /* Check that the result is valid and writable. */ | |
225 | struct mf_subfield dst = { .field = f, .ofs = ntohl(*ofsp), .n_bits = 1 }; | |
226 | ofperr = mf_check_dst(&dst, NULL); | |
227 | if (ofperr) { | |
228 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); | |
229 | VLOG_WARN_RL(&rl, "bad result bit (%s)", ofperr_to_string(ofperr)); | |
230 | goto exit; | |
231 | } | |
232 | ||
233 | if (!userdata->size) { | |
234 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); | |
235 | VLOG_WARN_RL(&rl, "DHCP options not present in the userdata"); | |
236 | goto exit; | |
237 | } | |
238 | ||
239 | /* Validate the DHCP request packet. | |
240 | * Format of the DHCP packet is | |
241 | * ------------------------------------------------------------------------ | |
242 | *| UDP HEADER | DHCP HEADER | 4 Byte DHCP Cookie | DHCP OPTIONS(var len)| | |
243 | * ------------------------------------------------------------------------ | |
244 | */ | |
245 | if (dp_packet_l4_size(pkt_in) < (UDP_HEADER_LEN + | |
246 | sizeof (struct dhcp_header) + sizeof(uint32_t) + 3)) { | |
247 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); | |
248 | VLOG_WARN_RL(&rl, "Invalid or incomplete DHCP packet recieved"); | |
249 | goto exit; | |
250 | } | |
251 | ||
252 | struct dhcp_header const *in_dhcp_data = dp_packet_get_udp_payload(pkt_in); | |
253 | if (in_dhcp_data->op != DHCP_OP_REQUEST) { | |
254 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); | |
255 | VLOG_WARN_RL(&rl, "Invalid opcode in the DHCP packet : %d", | |
256 | in_dhcp_data->op); | |
257 | goto exit; | |
258 | } | |
259 | ||
260 | /* DHCP options follow the DHCP header. The first 4 bytes of the DHCP | |
261 | * options is the DHCP magic cookie followed by the actual DHCP options. | |
262 | */ | |
263 | const uint8_t *in_dhcp_opt = | |
264 | (const uint8_t *)dp_packet_get_udp_payload(pkt_in) + | |
265 | sizeof (struct dhcp_header); | |
266 | ||
267 | ovs_be32 magic_cookie = htonl(DHCP_MAGIC_COOKIE); | |
268 | if (memcmp(in_dhcp_opt, &magic_cookie, sizeof(ovs_be32))) { | |
269 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); | |
270 | VLOG_WARN_RL(&rl, "DHCP magic cookie not present in the DHCP packet"); | |
271 | goto exit; | |
272 | } | |
273 | ||
274 | in_dhcp_opt += 4; | |
275 | /* Check that the DHCP Message Type (opt 53) is present or not with | |
276 | * valid values - DHCP_MSG_DISCOVER or DHCP_MSG_REQUEST as the first | |
277 | * DHCP option. | |
278 | */ | |
279 | if (!(in_dhcp_opt[0] == DHCP_OPT_MSG_TYPE && in_dhcp_opt[1] == 1 && ( | |
280 | in_dhcp_opt[2] == DHCP_MSG_DISCOVER || | |
281 | in_dhcp_opt[2] == DHCP_MSG_REQUEST))) { | |
282 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); | |
283 | VLOG_WARN_RL(&rl, "Invalid DHCP message type : opt code = %d," | |
284 | " opt value = %d", in_dhcp_opt[0], in_dhcp_opt[2]); | |
285 | goto exit; | |
286 | } | |
287 | ||
288 | uint8_t msg_type; | |
289 | if (in_dhcp_opt[2] == DHCP_MSG_DISCOVER) { | |
290 | msg_type = DHCP_MSG_OFFER; | |
291 | } else { | |
292 | msg_type = DHCP_MSG_ACK; | |
293 | } | |
294 | ||
295 | /* Frame the DHCP reply packet | |
296 | * Total DHCP options length will be options stored in the userdata + | |
297 | * 16 bytes. | |
298 | * | |
299 | * -------------------------------------------------------------- | |
300 | *| 4 Bytes (dhcp cookie) | 3 Bytes (option type) | DHCP options | | |
301 | * -------------------------------------------------------------- | |
302 | *| 4 Bytes padding | 1 Byte (option end 0xFF ) | 4 Bytes padding| | |
303 | * -------------------------------------------------------------- | |
304 | */ | |
305 | uint16_t new_l4_size = UDP_HEADER_LEN + DHCP_HEADER_LEN + \ | |
306 | userdata->size + 16; | |
307 | size_t new_packet_size = pkt_in->l4_ofs + new_l4_size; | |
308 | ||
309 | struct dp_packet pkt_out; | |
310 | dp_packet_init(&pkt_out, new_packet_size); | |
311 | dp_packet_clear(&pkt_out); | |
312 | dp_packet_prealloc_tailroom(&pkt_out, new_packet_size); | |
313 | pkt_out_ptr = &pkt_out; | |
314 | ||
315 | /* Copy the L2 and L3 headers from the pkt_in as they would remain same*/ | |
316 | dp_packet_put( | |
317 | &pkt_out, dp_packet_pull(pkt_in, pkt_in->l4_ofs), pkt_in->l4_ofs); | |
318 | ||
319 | pkt_out.l2_5_ofs = pkt_in->l2_5_ofs; | |
320 | pkt_out.l2_pad_size = pkt_in->l2_pad_size; | |
321 | pkt_out.l3_ofs = pkt_in->l3_ofs; | |
322 | pkt_out.l4_ofs = pkt_in->l4_ofs; | |
323 | ||
324 | struct udp_header *udp = dp_packet_put( | |
325 | &pkt_out, dp_packet_pull(pkt_in, UDP_HEADER_LEN), UDP_HEADER_LEN); | |
326 | ||
327 | struct dhcp_header *dhcp_data = dp_packet_put( | |
328 | &pkt_out, dp_packet_pull(pkt_in, DHCP_HEADER_LEN), DHCP_HEADER_LEN); | |
329 | dhcp_data->op = DHCP_OP_REPLY; | |
330 | dhcp_data->yiaddr = *offer_ip; | |
331 | dp_packet_put(&pkt_out, &magic_cookie, sizeof(ovs_be32)); | |
332 | ||
333 | uint8_t *out_dhcp_opts = dp_packet_put_zeros(&pkt_out, | |
334 | userdata->size + 12); | |
335 | /* DHCP option - type */ | |
336 | out_dhcp_opts[0] = DHCP_OPT_MSG_TYPE; | |
337 | out_dhcp_opts[1] = 1; | |
338 | out_dhcp_opts[2] = msg_type; | |
339 | out_dhcp_opts += 3; | |
340 | ||
341 | memcpy(out_dhcp_opts, userdata->data, userdata->size); | |
342 | out_dhcp_opts += userdata->size; | |
343 | /* Padding */ | |
344 | out_dhcp_opts += 4; | |
345 | /* End */ | |
346 | out_dhcp_opts[0] = DHCP_OPT_END; | |
347 | ||
348 | udp->udp_len = htons(new_l4_size); | |
349 | ||
350 | struct ip_header *out_ip = dp_packet_l3(&pkt_out); | |
351 | out_ip->ip_tot_len = htons(pkt_out.l4_ofs - pkt_out.l3_ofs + new_l4_size); | |
352 | udp->udp_csum = 0; | |
ece9c294 | 353 | /* Checksum needs to be initialized to zero. */ |
42814145 NS |
354 | out_ip->ip_csum = 0; |
355 | out_ip->ip_csum = csum(out_ip, sizeof *out_ip); | |
356 | ||
357 | pin->packet = dp_packet_data(&pkt_out); | |
358 | pin->packet_len = dp_packet_size(&pkt_out); | |
359 | ||
9240e9ab RR |
360 | /* Log the response. */ |
361 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(20, 40); | |
362 | const struct eth_header *l2 = dp_packet_l2(&pkt_out); | |
363 | VLOG_INFO_RL(&rl, "DHCP%s "ETH_ADDR_FMT" "IP_FMT"", | |
364 | msg_type == DHCP_MSG_OFFER ? "OFFER" : "ACK", | |
365 | ETH_ADDR_ARGS(l2->eth_src), IP_ARGS(*offer_ip)); | |
366 | ||
42814145 NS |
367 | success = 1; |
368 | exit: | |
369 | if (!ofperr) { | |
370 | union mf_subvalue sv; | |
371 | sv.u8_val = success; | |
372 | mf_write_subfield(&dst, &sv, &pin->flow_metadata); | |
373 | } | |
374 | queue_msg(ofputil_encode_resume(pin, continuation, proto)); | |
375 | if (pkt_out_ptr) { | |
376 | dp_packet_uninit(pkt_out_ptr); | |
377 | } | |
378 | } | |
379 | ||
01cfdb2f NS |
380 | static bool |
381 | compose_out_dhcpv6_opts(struct ofpbuf *userdata, | |
382 | struct ofpbuf *out_dhcpv6_opts, ovs_be32 iaid) | |
383 | { | |
384 | while (userdata->size) { | |
385 | struct dhcp_opt6_header *userdata_opt = ofpbuf_try_pull( | |
386 | userdata, sizeof *userdata_opt); | |
387 | if (!userdata_opt) { | |
388 | return false; | |
389 | } | |
390 | ||
a55dacac DDP |
391 | size_t size = ntohs(userdata_opt->size); |
392 | uint8_t *userdata_opt_data = ofpbuf_try_pull(userdata, size); | |
01cfdb2f NS |
393 | if (!userdata_opt_data) { |
394 | return false; | |
395 | } | |
396 | ||
a55dacac | 397 | switch (ntohs(userdata_opt->opt_code)) { |
01cfdb2f NS |
398 | case DHCPV6_OPT_SERVER_ID_CODE: |
399 | { | |
400 | /* The Server Identifier option carries a DUID | |
401 | * identifying a server between a client and a server. | |
402 | * See RFC 3315 Sec 9 and Sec 22.3. | |
403 | * | |
404 | * We use DUID Based on Link-layer Address [DUID-LL]. | |
405 | */ | |
406 | ||
407 | struct dhcpv6_opt_server_id *opt_server_id = ofpbuf_put_zeros( | |
408 | out_dhcpv6_opts, sizeof *opt_server_id); | |
409 | ||
410 | opt_server_id->opt.code = htons(DHCPV6_OPT_SERVER_ID_CODE); | |
a55dacac | 411 | opt_server_id->opt.len = htons(size + 4); |
01cfdb2f NS |
412 | opt_server_id->duid_type = htons(DHCPV6_DUID_LL); |
413 | opt_server_id->hw_type = htons(DHCPV6_HW_TYPE_ETH); | |
414 | memcpy(&opt_server_id->mac, userdata_opt_data, | |
415 | sizeof(struct eth_addr)); | |
416 | break; | |
417 | } | |
418 | ||
419 | case DHCPV6_OPT_IA_ADDR_CODE: | |
420 | { | |
a55dacac | 421 | if (size != sizeof(struct in6_addr)) { |
01cfdb2f NS |
422 | return false; |
423 | } | |
424 | ||
425 | /* IA Address option is used to specify IPv6 addresses associated | |
426 | * with an IA_NA or IA_TA. The IA Address option must be | |
427 | * encapsulated in the Options field of an IA_NA or IA_TA option. | |
428 | * | |
429 | * We will encapsulate the IA Address within the IA_NA option. | |
430 | * Please see RFC 3315 section 22.5 and 22.6 | |
431 | */ | |
432 | struct dhcpv6_opt_ia_na *opt_ia_na = ofpbuf_put_zeros( | |
433 | out_dhcpv6_opts, sizeof *opt_ia_na); | |
434 | opt_ia_na->opt.code = htons(DHCPV6_OPT_IA_NA_CODE); | |
435 | /* IA_NA length (in bytes)- | |
436 | * IAID - 4 | |
437 | * T1 - 4 | |
438 | * T2 - 4 | |
439 | * IA Address - sizeof(struct dhcpv6_opt_ia_addr) | |
440 | */ | |
441 | opt_ia_na->opt.len = htons(12 + sizeof(struct dhcpv6_opt_ia_addr)); | |
442 | opt_ia_na->iaid = iaid; | |
443 | /* Set the lifetime of the address(es) to infinity */ | |
444 | opt_ia_na->t1 = OVS_BE32_MAX; | |
445 | opt_ia_na->t2 = OVS_BE32_MAX; | |
446 | ||
447 | struct dhcpv6_opt_ia_addr *opt_ia_addr = ofpbuf_put_zeros( | |
448 | out_dhcpv6_opts, sizeof *opt_ia_addr); | |
449 | opt_ia_addr->opt.code = htons(DHCPV6_OPT_IA_ADDR_CODE); | |
a55dacac DDP |
450 | opt_ia_addr->opt.len = htons(size + 8); |
451 | memcpy(opt_ia_addr->ipv6.s6_addr, userdata_opt_data, size); | |
01cfdb2f NS |
452 | opt_ia_addr->t1 = OVS_BE32_MAX; |
453 | opt_ia_addr->t2 = OVS_BE32_MAX; | |
454 | break; | |
455 | } | |
456 | ||
457 | case DHCPV6_OPT_DNS_SERVER_CODE: | |
458 | { | |
459 | struct dhcpv6_opt_header *opt_dns = ofpbuf_put_zeros( | |
460 | out_dhcpv6_opts, sizeof *opt_dns); | |
461 | opt_dns->code = htons(DHCPV6_OPT_DNS_SERVER_CODE); | |
a55dacac DDP |
462 | opt_dns->len = htons(size); |
463 | ofpbuf_put(out_dhcpv6_opts, userdata_opt_data, size); | |
01cfdb2f NS |
464 | break; |
465 | } | |
466 | ||
467 | case DHCPV6_OPT_DOMAIN_SEARCH_CODE: | |
468 | { | |
469 | struct dhcpv6_opt_header *opt_dsl = ofpbuf_put_zeros( | |
470 | out_dhcpv6_opts, sizeof *opt_dsl); | |
471 | opt_dsl->code = htons(DHCPV6_OPT_DOMAIN_SEARCH_CODE); | |
a55dacac DDP |
472 | opt_dsl->len = htons(size + 2); |
473 | uint8_t *data = ofpbuf_put_zeros(out_dhcpv6_opts, size + 2); | |
474 | *data = size; | |
475 | memcpy(data + 1, userdata_opt_data, size); | |
01cfdb2f NS |
476 | break; |
477 | } | |
478 | ||
479 | default: | |
480 | return false; | |
481 | } | |
482 | } | |
483 | return true; | |
484 | } | |
485 | ||
486 | static void | |
487 | pinctrl_handle_put_dhcpv6_opts( | |
488 | struct dp_packet *pkt_in, struct ofputil_packet_in *pin, | |
489 | struct ofpbuf *userdata, struct ofpbuf *continuation OVS_UNUSED) | |
490 | { | |
491 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); | |
492 | enum ofp_version version = rconn_get_version(swconn); | |
493 | enum ofputil_protocol proto = ofputil_protocol_from_ofp_version(version); | |
494 | struct dp_packet *pkt_out_ptr = NULL; | |
495 | uint32_t success = 0; | |
496 | ||
497 | /* Parse result field. */ | |
498 | const struct mf_field *f; | |
04f48a68 | 499 | enum ofperr ofperr = nx_pull_header(userdata, NULL, &f, NULL); |
01cfdb2f NS |
500 | if (ofperr) { |
501 | VLOG_WARN_RL(&rl, "bad result OXM (%s)", ofperr_to_string(ofperr)); | |
502 | goto exit; | |
503 | } | |
504 | ||
505 | /* Parse result offset. */ | |
506 | ovs_be32 *ofsp = ofpbuf_try_pull(userdata, sizeof *ofsp); | |
507 | if (!ofsp) { | |
508 | VLOG_WARN_RL(&rl, "offset not present in the userdata"); | |
509 | goto exit; | |
510 | } | |
511 | ||
512 | /* Check that the result is valid and writable. */ | |
513 | struct mf_subfield dst = { .field = f, .ofs = ntohl(*ofsp), .n_bits = 1 }; | |
514 | ofperr = mf_check_dst(&dst, NULL); | |
515 | if (ofperr) { | |
516 | VLOG_WARN_RL(&rl, "bad result bit (%s)", ofperr_to_string(ofperr)); | |
517 | goto exit; | |
518 | } | |
519 | ||
520 | if (!userdata->size) { | |
521 | VLOG_WARN_RL(&rl, "DHCPv6 options not present in the userdata"); | |
522 | goto exit; | |
523 | } | |
524 | ||
525 | struct udp_header *in_udp = dp_packet_l4(pkt_in); | |
526 | const uint8_t *in_dhcpv6_data = dp_packet_get_udp_payload(pkt_in); | |
527 | uint8_t out_dhcpv6_msg_type; | |
528 | switch(*in_dhcpv6_data) { | |
529 | case DHCPV6_MSG_TYPE_SOLICIT: | |
530 | out_dhcpv6_msg_type = DHCPV6_MSG_TYPE_ADVT; | |
531 | break; | |
532 | ||
533 | case DHCPV6_MSG_TYPE_REQUEST: | |
534 | case DHCPV6_MSG_TYPE_CONFIRM: | |
535 | case DHCPV6_MSG_TYPE_DECLINE: | |
536 | out_dhcpv6_msg_type = DHCPV6_MSG_TYPE_REPLY; | |
537 | break; | |
538 | ||
539 | default: | |
540 | /* Invalid or unsupported DHCPv6 message type */ | |
541 | goto exit; | |
542 | } | |
543 | ||
544 | /* Skip 4 bytes (message type (1 byte) + transaction ID (3 bytes). */ | |
545 | in_dhcpv6_data += 4; | |
546 | /* We need to extract IAID from the IA-NA option of the client's DHCPv6 | |
547 | * solicit/request/confirm packet and copy the same IAID in the Server's | |
548 | * response. */ | |
549 | ovs_be32 iaid = 0; | |
550 | struct dhcpv6_opt_header const *in_opt_client_id = NULL; | |
551 | size_t udp_len = ntohs(in_udp->udp_len); | |
552 | size_t l4_len = dp_packet_l4_size(pkt_in); | |
553 | uint8_t *end = (uint8_t *)in_udp + MIN(udp_len, l4_len); | |
554 | while (in_dhcpv6_data < end) { | |
555 | struct dhcpv6_opt_header const *in_opt = | |
556 | (struct dhcpv6_opt_header *)in_dhcpv6_data; | |
557 | switch(ntohs(in_opt->code)) { | |
558 | case DHCPV6_OPT_IA_NA_CODE: | |
559 | { | |
560 | struct dhcpv6_opt_ia_na *opt_ia_na = ( | |
561 | struct dhcpv6_opt_ia_na *)in_opt; | |
562 | iaid = opt_ia_na->iaid; | |
563 | break; | |
564 | } | |
565 | ||
566 | case DHCPV6_OPT_CLIENT_ID_CODE: | |
567 | in_opt_client_id = in_opt; | |
568 | break; | |
569 | ||
570 | default: | |
571 | break; | |
572 | } | |
573 | in_dhcpv6_data += sizeof *in_opt + ntohs(in_opt->len); | |
574 | } | |
575 | ||
576 | if (!in_opt_client_id) { | |
577 | VLOG_WARN_RL(&rl, "DHCPv6 option - Client id not present in the " | |
578 | " DHCPv6 packet"); | |
579 | goto exit; | |
580 | } | |
581 | ||
582 | if (!iaid) { | |
583 | VLOG_WARN_RL(&rl, "DHCPv6 option - IA NA not present in the " | |
584 | " DHCPv6 packet"); | |
585 | goto exit; | |
586 | } | |
587 | ||
588 | uint64_t out_ofpacts_dhcpv6_opts_stub[256 / 8]; | |
589 | struct ofpbuf out_dhcpv6_opts = | |
590 | OFPBUF_STUB_INITIALIZER(out_ofpacts_dhcpv6_opts_stub); | |
591 | ||
592 | if (!compose_out_dhcpv6_opts(userdata, &out_dhcpv6_opts, iaid)) { | |
593 | VLOG_WARN_RL(&rl, "Invalid userdata"); | |
594 | goto exit; | |
595 | } | |
596 | ||
597 | uint16_t new_l4_size | |
598 | = (UDP_HEADER_LEN + 4 + sizeof *in_opt_client_id + | |
599 | ntohs(in_opt_client_id->len) + out_dhcpv6_opts.size); | |
600 | size_t new_packet_size = pkt_in->l4_ofs + new_l4_size; | |
601 | ||
602 | struct dp_packet pkt_out; | |
603 | dp_packet_init(&pkt_out, new_packet_size); | |
604 | dp_packet_clear(&pkt_out); | |
605 | dp_packet_prealloc_tailroom(&pkt_out, new_packet_size); | |
606 | pkt_out_ptr = &pkt_out; | |
607 | ||
608 | /* Copy L2 and L3 headers from pkt_in. */ | |
609 | dp_packet_put(&pkt_out, dp_packet_pull(pkt_in, pkt_in->l4_ofs), | |
610 | pkt_in->l4_ofs); | |
611 | ||
612 | pkt_out.l2_5_ofs = pkt_in->l2_5_ofs; | |
613 | pkt_out.l2_pad_size = pkt_in->l2_pad_size; | |
614 | pkt_out.l3_ofs = pkt_in->l3_ofs; | |
615 | pkt_out.l4_ofs = pkt_in->l4_ofs; | |
616 | ||
617 | /* Pull the DHCPv6 message type and transaction id from the pkt_in. | |
618 | * Need to preserve the transaction id in the DHCPv6 reply packet. */ | |
619 | struct udp_header *out_udp = dp_packet_put( | |
620 | &pkt_out, dp_packet_pull(pkt_in, UDP_HEADER_LEN), UDP_HEADER_LEN); | |
621 | uint8_t *out_dhcpv6 = dp_packet_put(&pkt_out, dp_packet_pull(pkt_in, 4), 4); | |
622 | ||
623 | /* Set the proper DHCPv6 message type. */ | |
624 | *out_dhcpv6 = out_dhcpv6_msg_type; | |
625 | ||
626 | /* Copy the Client Identifier. */ | |
627 | dp_packet_put(&pkt_out, in_opt_client_id, | |
628 | sizeof *in_opt_client_id + ntohs(in_opt_client_id->len)); | |
629 | ||
630 | /* Copy the DHCPv6 Options. */ | |
631 | dp_packet_put(&pkt_out, out_dhcpv6_opts.data, out_dhcpv6_opts.size); | |
632 | out_udp->udp_len = htons(new_l4_size); | |
633 | out_udp->udp_csum = 0; | |
634 | ||
635 | struct ovs_16aligned_ip6_hdr *out_ip6 = dp_packet_l3(&pkt_out); | |
636 | out_ip6->ip6_ctlun.ip6_un1.ip6_un1_plen = out_udp->udp_len; | |
637 | ||
638 | uint32_t csum; | |
639 | csum = packet_csum_pseudoheader6(dp_packet_l3(&pkt_out)); | |
640 | csum = csum_continue(csum, out_udp, dp_packet_size(&pkt_out) - | |
641 | ((const unsigned char *)out_udp - | |
642 | (const unsigned char *)dp_packet_l2(&pkt_out))); | |
643 | out_udp->udp_csum = csum_finish(csum); | |
644 | if (!out_udp->udp_csum) { | |
645 | out_udp->udp_csum = htons(0xffff); | |
646 | } | |
647 | ||
648 | pin->packet = dp_packet_data(&pkt_out); | |
649 | pin->packet_len = dp_packet_size(&pkt_out); | |
650 | ofpbuf_uninit(&out_dhcpv6_opts); | |
651 | success = 1; | |
652 | exit: | |
653 | if (!ofperr) { | |
654 | union mf_subvalue sv; | |
655 | sv.u8_val = success; | |
656 | mf_write_subfield(&dst, &sv, &pin->flow_metadata); | |
657 | } | |
658 | queue_msg(ofputil_encode_resume(pin, continuation, proto)); | |
659 | dp_packet_uninit(pkt_out_ptr); | |
660 | } | |
661 | ||
6335d074 BP |
662 | static void |
663 | process_packet_in(const struct ofp_header *msg) | |
664 | { | |
665 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); | |
666 | ||
667 | struct ofputil_packet_in pin; | |
42814145 | 668 | struct ofpbuf continuation; |
3cddeff0 | 669 | enum ofperr error = ofputil_decode_packet_in(msg, true, NULL, NULL, &pin, |
42814145 NS |
670 | NULL, NULL, &continuation); |
671 | ||
6335d074 BP |
672 | if (error) { |
673 | VLOG_WARN_RL(&rl, "error decoding packet-in: %s", | |
674 | ofperr_to_string(error)); | |
27732ac4 BS |
675 | return; |
676 | } | |
677 | if (pin.reason != OFPR_ACTION) { | |
678 | return; | |
679 | } | |
680 | ||
6335d074 BP |
681 | struct ofpbuf userdata = ofpbuf_const_initializer(pin.userdata, |
682 | pin.userdata_len); | |
683 | const struct action_header *ah = ofpbuf_pull(&userdata, sizeof *ah); | |
684 | if (!ah) { | |
685 | VLOG_WARN_RL(&rl, "packet-in userdata lacks action header"); | |
686 | return; | |
687 | } | |
688 | ||
689 | struct dp_packet packet; | |
690 | dp_packet_use_const(&packet, pin.packet, pin.packet_len); | |
691 | struct flow headers; | |
692 | flow_extract(&packet, &headers); | |
693 | ||
6335d074 BP |
694 | switch (ntohl(ah->opcode)) { |
695 | case ACTION_OPCODE_ARP: | |
0bac7164 BP |
696 | pinctrl_handle_arp(&headers, &pin.flow_metadata, &userdata); |
697 | break; | |
698 | ||
699 | case ACTION_OPCODE_PUT_ARP: | |
c34a87b6 JP |
700 | pinctrl_handle_put_mac_binding(&pin.flow_metadata.flow, &headers, |
701 | true); | |
6335d074 BP |
702 | break; |
703 | ||
42814145 NS |
704 | case ACTION_OPCODE_PUT_DHCP_OPTS: |
705 | pinctrl_handle_put_dhcp_opts(&packet, &pin, &userdata, &continuation); | |
706 | break; | |
707 | ||
f8a8db39 JP |
708 | case ACTION_OPCODE_ND_NA: |
709 | pinctrl_handle_nd_na(&headers, &pin.flow_metadata, &userdata); | |
e75451fe ZKL |
710 | break; |
711 | ||
c34a87b6 JP |
712 | case ACTION_OPCODE_PUT_ND: |
713 | pinctrl_handle_put_mac_binding(&pin.flow_metadata.flow, &headers, | |
714 | false); | |
715 | break; | |
716 | ||
01cfdb2f NS |
717 | case ACTION_OPCODE_PUT_DHCPV6_OPTS: |
718 | pinctrl_handle_put_dhcpv6_opts(&packet, &pin, &userdata, | |
719 | &continuation); | |
720 | break; | |
721 | ||
6335d074 | 722 | default: |
0bac7164 BP |
723 | VLOG_WARN_RL(&rl, "unrecognized packet-in opcode %"PRIu32, |
724 | ntohl(ah->opcode)); | |
6335d074 BP |
725 | break; |
726 | } | |
27732ac4 BS |
727 | } |
728 | ||
729 | static void | |
6335d074 | 730 | pinctrl_recv(const struct ofp_header *oh, enum ofptype type) |
27732ac4 BS |
731 | { |
732 | if (type == OFPTYPE_ECHO_REQUEST) { | |
733 | queue_msg(make_echo_reply(oh)); | |
734 | } else if (type == OFPTYPE_GET_CONFIG_REPLY) { | |
7c9afefd | 735 | /* Enable asynchronous messages */ |
660f5a61 | 736 | struct ofputil_switch_config config; |
27732ac4 | 737 | |
660f5a61 | 738 | ofputil_decode_get_config_reply(oh, &config); |
3ca6cc76 | 739 | config.miss_send_len = UINT16_MAX; |
27732ac4 BS |
740 | set_switch_config(swconn, &config); |
741 | } else if (type == OFPTYPE_PACKET_IN) { | |
6335d074 | 742 | process_packet_in(oh); |
27732ac4 BS |
743 | } else if (type != OFPTYPE_ECHO_REPLY && type != OFPTYPE_BARRIER_REPLY) { |
744 | if (VLOG_IS_DBG_ENABLED()) { | |
745 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30, 300); | |
746 | ||
747 | char *s = ofp_to_string(oh, ntohs(oh->length), 2); | |
748 | ||
749 | VLOG_DBG_RL(&rl, "OpenFlow packet ignored: %s", s); | |
750 | free(s); | |
751 | } | |
752 | } | |
753 | } | |
754 | ||
755 | void | |
0bac7164 | 756 | pinctrl_run(struct controller_ctx *ctx, const struct lport_index *lports, |
0ee8aaf6 | 757 | const struct ovsrec_bridge *br_int, |
f1a8bd06 | 758 | const struct sbrec_chassis *chassis, |
0ee8aaf6 | 759 | struct hmap *local_datapaths) |
27732ac4 | 760 | { |
3d0b8206 | 761 | char *target = xasprintf("unix:%s/%s.mgmt", ovs_rundir(), br_int->name); |
762 | if (strcmp(target, rconn_get_target(swconn))) { | |
763 | VLOG_INFO("%s: connecting to switch", target); | |
764 | rconn_connect(swconn, target, target); | |
27732ac4 | 765 | } |
3d0b8206 | 766 | free(target); |
27732ac4 BS |
767 | |
768 | rconn_run(swconn); | |
769 | ||
6335d074 BP |
770 | if (rconn_is_connected(swconn)) { |
771 | if (conn_seq_no != rconn_get_connection_seqno(swconn)) { | |
772 | pinctrl_setup(swconn); | |
773 | conn_seq_no = rconn_get_connection_seqno(swconn); | |
c34a87b6 | 774 | flush_put_mac_bindings(); |
6335d074 | 775 | } |
27732ac4 | 776 | |
6335d074 BP |
777 | /* Process a limited number of messages per call. */ |
778 | for (int i = 0; i < 50; i++) { | |
779 | struct ofpbuf *msg = rconn_recv(swconn); | |
780 | if (!msg) { | |
781 | break; | |
782 | } | |
27732ac4 | 783 | |
6335d074 BP |
784 | const struct ofp_header *oh = msg->data; |
785 | enum ofptype type; | |
27732ac4 | 786 | |
6335d074 BP |
787 | ofptype_decode(&type, oh); |
788 | pinctrl_recv(oh, type); | |
789 | ofpbuf_delete(msg); | |
790 | } | |
27732ac4 | 791 | } |
0bac7164 | 792 | |
c34a87b6 | 793 | run_put_mac_bindings(ctx, lports); |
f1a8bd06 | 794 | send_garp_run(br_int, chassis, lports, local_datapaths); |
27732ac4 BS |
795 | } |
796 | ||
797 | void | |
0bac7164 | 798 | pinctrl_wait(struct controller_ctx *ctx) |
27732ac4 | 799 | { |
c34a87b6 | 800 | wait_put_mac_bindings(ctx); |
27732ac4 BS |
801 | rconn_run_wait(swconn); |
802 | rconn_recv_wait(swconn); | |
0ee8aaf6 | 803 | send_garp_wait(); |
27732ac4 BS |
804 | } |
805 | ||
806 | void | |
807 | pinctrl_destroy(void) | |
808 | { | |
809 | rconn_destroy(swconn); | |
c34a87b6 | 810 | destroy_put_mac_bindings(); |
0ee8aaf6 | 811 | destroy_send_garps(); |
0bac7164 BP |
812 | } |
813 | \f | |
c34a87b6 JP |
814 | /* Implementation of the "put_arp" and "put_nd" OVN actions. These |
815 | * actions send a packet to ovn-controller, using the flow as an API | |
816 | * (see actions.h for details). This code implements the actions by | |
817 | * updating the MAC_Binding table in the southbound database. | |
0bac7164 BP |
818 | * |
819 | * This code could be a lot simpler if the database could always be updated, | |
820 | * but in fact we can only update it when ctx->ovnsb_idl_txn is nonnull. Thus, | |
c34a87b6 JP |
821 | * we buffer up a few put_mac_bindings (but we don't keep them longer |
822 | * than 1 second) and apply them whenever a database transaction is | |
823 | * available. */ | |
0bac7164 | 824 | |
c34a87b6 JP |
825 | /* Buffered "put_mac_binding" operation. */ |
826 | struct put_mac_binding { | |
827 | struct hmap_node hmap_node; /* In 'put_mac_bindings'. */ | |
0bac7164 BP |
828 | |
829 | long long int timestamp; /* In milliseconds. */ | |
830 | ||
831 | /* Key. */ | |
832 | uint32_t dp_key; | |
833 | uint32_t port_key; | |
c34a87b6 | 834 | char ip_s[INET6_ADDRSTRLEN + 1]; |
0bac7164 BP |
835 | |
836 | /* Value. */ | |
837 | struct eth_addr mac; | |
838 | }; | |
839 | ||
c34a87b6 JP |
840 | /* Contains "struct put_mac_binding"s. */ |
841 | static struct hmap put_mac_bindings; | |
0bac7164 BP |
842 | |
843 | static void | |
c34a87b6 | 844 | init_put_mac_bindings(void) |
0bac7164 | 845 | { |
c34a87b6 | 846 | hmap_init(&put_mac_bindings); |
0bac7164 BP |
847 | } |
848 | ||
849 | static void | |
c34a87b6 | 850 | destroy_put_mac_bindings(void) |
0bac7164 | 851 | { |
c34a87b6 JP |
852 | flush_put_mac_bindings(); |
853 | hmap_destroy(&put_mac_bindings); | |
0bac7164 BP |
854 | } |
855 | ||
c34a87b6 JP |
856 | static struct put_mac_binding * |
857 | pinctrl_find_put_mac_binding(uint32_t dp_key, uint32_t port_key, | |
858 | const char *ip_s, uint32_t hash) | |
0bac7164 | 859 | { |
c34a87b6 JP |
860 | struct put_mac_binding *pa; |
861 | HMAP_FOR_EACH_WITH_HASH (pa, hmap_node, hash, &put_mac_bindings) { | |
0bac7164 BP |
862 | if (pa->dp_key == dp_key |
863 | && pa->port_key == port_key | |
c34a87b6 | 864 | && !strcmp(pa->ip_s, ip_s)) { |
0bac7164 BP |
865 | return pa; |
866 | } | |
867 | } | |
868 | return NULL; | |
869 | } | |
870 | ||
871 | static void | |
c34a87b6 JP |
872 | pinctrl_handle_put_mac_binding(const struct flow *md, |
873 | const struct flow *headers, bool is_arp) | |
0bac7164 BP |
874 | { |
875 | uint32_t dp_key = ntohll(md->metadata); | |
876 | uint32_t port_key = md->regs[MFF_LOG_INPORT - MFF_REG0]; | |
c34a87b6 JP |
877 | char ip_s[INET6_ADDRSTRLEN]; |
878 | ||
879 | if (is_arp) { | |
880 | ovs_be32 ip = htonl(md->regs[0]); | |
881 | inet_ntop(AF_INET, &ip, ip_s, sizeof(ip_s)); | |
882 | } else { | |
883 | ovs_be128 ip6 = hton128(flow_get_xxreg(md, 0)); | |
884 | inet_ntop(AF_INET6, &ip6, ip_s, sizeof(ip_s)); | |
885 | } | |
886 | uint32_t hash = hash_string(ip_s, hash_2words(dp_key, port_key)); | |
887 | struct put_mac_binding *pmb | |
888 | = pinctrl_find_put_mac_binding(dp_key, port_key, ip_s, hash); | |
889 | if (!pmb) { | |
890 | if (hmap_count(&put_mac_bindings) >= 1000) { | |
891 | COVERAGE_INC(pinctrl_drop_put_mac_binding); | |
0bac7164 BP |
892 | return; |
893 | } | |
894 | ||
c34a87b6 JP |
895 | pmb = xmalloc(sizeof *pmb); |
896 | hmap_insert(&put_mac_bindings, &pmb->hmap_node, hash); | |
897 | pmb->dp_key = dp_key; | |
898 | pmb->port_key = port_key; | |
899 | ovs_strlcpy(pmb->ip_s, ip_s, sizeof pmb->ip_s); | |
0bac7164 | 900 | } |
c34a87b6 JP |
901 | pmb->timestamp = time_msec(); |
902 | pmb->mac = headers->dl_src; | |
0bac7164 BP |
903 | } |
904 | ||
905 | static void | |
c34a87b6 JP |
906 | run_put_mac_binding(struct controller_ctx *ctx, |
907 | const struct lport_index *lports, | |
908 | const struct put_mac_binding *pmb) | |
0bac7164 | 909 | { |
c34a87b6 | 910 | if (time_msec() > pmb->timestamp + 1000) { |
0bac7164 BP |
911 | return; |
912 | } | |
913 | ||
914 | /* Convert logical datapath and logical port key into lport. */ | |
915 | const struct sbrec_port_binding *pb | |
c34a87b6 | 916 | = lport_lookup_by_key(lports, pmb->dp_key, pmb->port_key); |
0bac7164 BP |
917 | if (!pb) { |
918 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); | |
919 | ||
920 | VLOG_WARN_RL(&rl, "unknown logical port with datapath %"PRIu32" " | |
c34a87b6 | 921 | "and port %"PRIu32, pmb->dp_key, pmb->port_key); |
0bac7164 BP |
922 | return; |
923 | } | |
924 | ||
c34a87b6 | 925 | /* Convert ethernet argument to string form for database. */ |
0bac7164 BP |
926 | char mac_string[ETH_ADDR_STRLEN + 1]; |
927 | snprintf(mac_string, sizeof mac_string, | |
c34a87b6 | 928 | ETH_ADDR_FMT, ETH_ADDR_ARGS(pmb->mac)); |
0bac7164 | 929 | |
c34a87b6 | 930 | /* Check for an update an existing IP-MAC binding for this logical |
0bac7164 BP |
931 | * port. |
932 | * | |
933 | * XXX This is not very efficient. */ | |
934 | const struct sbrec_mac_binding *b; | |
935 | SBREC_MAC_BINDING_FOR_EACH (b, ctx->ovnsb_idl) { | |
936 | if (!strcmp(b->logical_port, pb->logical_port) | |
c34a87b6 | 937 | && !strcmp(b->ip, pmb->ip_s)) { |
0bac7164 BP |
938 | if (strcmp(b->mac, mac_string)) { |
939 | sbrec_mac_binding_set_mac(b, mac_string); | |
940 | } | |
941 | return; | |
942 | } | |
943 | } | |
944 | ||
945 | /* Add new IP-MAC binding for this logical port. */ | |
946 | b = sbrec_mac_binding_insert(ctx->ovnsb_idl_txn); | |
947 | sbrec_mac_binding_set_logical_port(b, pb->logical_port); | |
c34a87b6 | 948 | sbrec_mac_binding_set_ip(b, pmb->ip_s); |
0bac7164 | 949 | sbrec_mac_binding_set_mac(b, mac_string); |
791a7747 | 950 | sbrec_mac_binding_set_datapath(b, pb->datapath); |
0bac7164 BP |
951 | } |
952 | ||
953 | static void | |
c34a87b6 JP |
954 | run_put_mac_bindings(struct controller_ctx *ctx, |
955 | const struct lport_index *lports) | |
0bac7164 BP |
956 | { |
957 | if (!ctx->ovnsb_idl_txn) { | |
958 | return; | |
959 | } | |
960 | ||
c34a87b6 JP |
961 | const struct put_mac_binding *pmb; |
962 | HMAP_FOR_EACH (pmb, hmap_node, &put_mac_bindings) { | |
963 | run_put_mac_binding(ctx, lports, pmb); | |
0bac7164 | 964 | } |
c34a87b6 | 965 | flush_put_mac_bindings(); |
0bac7164 BP |
966 | } |
967 | ||
968 | static void | |
c34a87b6 | 969 | wait_put_mac_bindings(struct controller_ctx *ctx) |
0bac7164 | 970 | { |
c34a87b6 | 971 | if (ctx->ovnsb_idl_txn && !hmap_is_empty(&put_mac_bindings)) { |
0bac7164 BP |
972 | poll_immediate_wake(); |
973 | } | |
974 | } | |
975 | ||
976 | static void | |
c34a87b6 | 977 | flush_put_mac_bindings(void) |
0bac7164 | 978 | { |
c34a87b6 JP |
979 | struct put_mac_binding *pmb; |
980 | HMAP_FOR_EACH_POP (pmb, hmap_node, &put_mac_bindings) { | |
981 | free(pmb); | |
0bac7164 | 982 | } |
27732ac4 | 983 | } |
0ee8aaf6 RR |
984 | \f |
985 | /* | |
986 | * Send gratuitous ARP for vif on localnet. | |
987 | * | |
988 | * When a new vif on localnet is added, gratuitous ARPs are sent announcing | |
989 | * the port's mac,ip mapping. On localnet, such announcements are needed for | |
990 | * switches and routers on the broadcast segment to update their port-mac | |
991 | * and ARP tables. | |
992 | */ | |
993 | struct garp_data { | |
994 | struct eth_addr ea; /* Ethernet address of port. */ | |
995 | ovs_be32 ipv4; /* Ipv4 address of port. */ | |
996 | long long int announce_time; /* Next announcement in ms. */ | |
997 | int backoff; /* Backoff for the next announcement. */ | |
58d04fb1 | 998 | ofp_port_t ofport; /* ofport used to output this GARP. */ |
0ee8aaf6 RR |
999 | }; |
1000 | ||
1001 | /* Contains GARPs to be sent. */ | |
1002 | static struct shash send_garp_data; | |
1003 | ||
1004 | /* Next GARP announcement in ms. */ | |
1005 | static long long int send_garp_time; | |
1006 | ||
1007 | static void | |
1008 | init_send_garps(void) | |
1009 | { | |
1010 | shash_init(&send_garp_data); | |
1011 | send_garp_time = LLONG_MAX; | |
1012 | } | |
1013 | ||
1014 | static void | |
1015 | destroy_send_garps(void) | |
1016 | { | |
1017 | shash_destroy_free_data(&send_garp_data); | |
1018 | } | |
1019 | ||
8439c2eb CSV |
1020 | static void |
1021 | add_garp(const char *name, ofp_port_t ofport, | |
1022 | const struct eth_addr ea, ovs_be32 ip) | |
1023 | { | |
1024 | struct garp_data *garp = xmalloc(sizeof *garp); | |
1025 | garp->ea = ea; | |
1026 | garp->ipv4 = ip; | |
1027 | garp->announce_time = time_msec() + 1000; | |
1028 | garp->backoff = 1; | |
1029 | garp->ofport = ofport; | |
1030 | shash_add(&send_garp_data, name, garp); | |
1031 | } | |
1032 | ||
0ee8aaf6 RR |
1033 | /* Add or update a vif for which GARPs need to be announced. */ |
1034 | static void | |
1035 | send_garp_update(const struct sbrec_port_binding *binding_rec, | |
8439c2eb CSV |
1036 | struct simap *localnet_ofports, struct hmap *local_datapaths, |
1037 | struct shash *nat_addresses) | |
0ee8aaf6 RR |
1038 | { |
1039 | /* Find the localnet ofport to send this GARP. */ | |
1040 | struct local_datapath *ld | |
1041 | = get_local_datapath(local_datapaths, | |
1042 | binding_rec->datapath->tunnel_key); | |
1043 | if (!ld || !ld->localnet_port) { | |
1044 | return; | |
1045 | } | |
58d04fb1 BP |
1046 | ofp_port_t ofport = u16_to_ofp(simap_get(localnet_ofports, |
1047 | ld->localnet_port->logical_port)); | |
0ee8aaf6 | 1048 | |
8439c2eb CSV |
1049 | volatile struct garp_data *garp = NULL; |
1050 | /* Update GARP for NAT IP if it exists. */ | |
1051 | if (!strcmp(binding_rec->type, "l3gateway")) { | |
1052 | struct lport_addresses *laddrs = NULL; | |
1053 | laddrs = shash_find_data(nat_addresses, binding_rec->logical_port); | |
1054 | if (!laddrs) { | |
1055 | return; | |
1056 | } | |
1057 | int i; | |
1058 | for (i = 0; i < laddrs->n_ipv4_addrs; i++) { | |
1059 | char *name = xasprintf("%s-%s", binding_rec->logical_port, | |
1060 | laddrs->ipv4_addrs[i].addr_s); | |
1061 | garp = shash_find_data(&send_garp_data, name); | |
1062 | if (garp) { | |
1063 | garp->ofport = ofport; | |
1064 | } else { | |
1065 | add_garp(name, ofport, laddrs->ea, laddrs->ipv4_addrs[i].addr); | |
1066 | } | |
1067 | free(name); | |
1068 | } | |
8439c2eb CSV |
1069 | return; |
1070 | } | |
1071 | ||
1072 | /* Update GARP for vif if it exists. */ | |
1073 | garp = shash_find_data(&send_garp_data, binding_rec->logical_port); | |
0ee8aaf6 RR |
1074 | if (garp) { |
1075 | garp->ofport = ofport; | |
1076 | return; | |
1077 | } | |
1078 | ||
1079 | /* Add GARP for new vif. */ | |
1080 | int i; | |
1081 | for (i = 0; i < binding_rec->n_mac; i++) { | |
1082 | struct lport_addresses laddrs; | |
5942a98d | 1083 | if (!extract_lsp_addresses(binding_rec->mac[i], &laddrs) |
0ee8aaf6 RR |
1084 | || !laddrs.n_ipv4_addrs) { |
1085 | continue; | |
1086 | } | |
1087 | ||
8439c2eb CSV |
1088 | add_garp(binding_rec->logical_port, ofport, |
1089 | laddrs.ea, laddrs.ipv4_addrs[0].addr); | |
0ee8aaf6 | 1090 | |
f2a715b5 | 1091 | destroy_lport_addresses(&laddrs); |
0ee8aaf6 RR |
1092 | break; |
1093 | } | |
1094 | } | |
1095 | ||
1096 | /* Remove a vif from GARP announcements. */ | |
1097 | static void | |
1098 | send_garp_delete(const char *lport) | |
1099 | { | |
1100 | struct garp_data *garp = shash_find_and_delete(&send_garp_data, lport); | |
1101 | free(garp); | |
1102 | } | |
1103 | ||
1104 | static long long int | |
1105 | send_garp(struct garp_data *garp, long long int current_time) | |
1106 | { | |
1107 | if (current_time < garp->announce_time) { | |
1108 | return garp->announce_time; | |
1109 | } | |
1110 | ||
1111 | /* Compose a GARP request packet. */ | |
1112 | uint64_t packet_stub[128 / 8]; | |
1113 | struct dp_packet packet; | |
1114 | dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub); | |
1115 | compose_arp(&packet, ARP_OP_REQUEST, garp->ea, eth_addr_zero, | |
1116 | true, garp->ipv4, garp->ipv4); | |
1117 | ||
1118 | /* Compose actions. The garp request is output on localnet ofport. */ | |
1119 | uint64_t ofpacts_stub[4096 / 8]; | |
1120 | struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub); | |
1121 | enum ofp_version version = rconn_get_version(swconn); | |
1122 | ofpact_put_OUTPUT(&ofpacts)->port = garp->ofport; | |
1123 | ||
1124 | struct ofputil_packet_out po = { | |
1125 | .packet = dp_packet_data(&packet), | |
1126 | .packet_len = dp_packet_size(&packet), | |
1127 | .buffer_id = UINT32_MAX, | |
1128 | .in_port = OFPP_CONTROLLER, | |
1129 | .ofpacts = ofpacts.data, | |
1130 | .ofpacts_len = ofpacts.size, | |
1131 | }; | |
1132 | enum ofputil_protocol proto = ofputil_protocol_from_ofp_version(version); | |
1133 | queue_msg(ofputil_encode_packet_out(&po, proto)); | |
1134 | dp_packet_uninit(&packet); | |
1135 | ofpbuf_uninit(&ofpacts); | |
1136 | ||
1137 | /* Set the next announcement. At most 5 announcements are sent for a | |
1138 | * vif. */ | |
1139 | if (garp->backoff < 16) { | |
1140 | garp->backoff *= 2; | |
1141 | garp->announce_time = current_time + garp->backoff * 1000; | |
1142 | } else { | |
1143 | garp->announce_time = LLONG_MAX; | |
1144 | } | |
1145 | return garp->announce_time; | |
1146 | } | |
1147 | ||
8439c2eb | 1148 | /* Get localnet vifs, local l3gw ports and ofport for localnet patch ports. */ |
0ee8aaf6 | 1149 | static void |
8439c2eb | 1150 | get_localnet_vifs_l3gwports(const struct ovsrec_bridge *br_int, |
f1a8bd06 | 1151 | const struct sbrec_chassis *chassis, |
0ee8aaf6 RR |
1152 | const struct lport_index *lports, |
1153 | struct hmap *local_datapaths, | |
1154 | struct sset *localnet_vifs, | |
8439c2eb CSV |
1155 | struct simap *localnet_ofports, |
1156 | struct sset *local_l3gw_ports) | |
0ee8aaf6 RR |
1157 | { |
1158 | for (int i = 0; i < br_int->n_ports; i++) { | |
1159 | const struct ovsrec_port *port_rec = br_int->ports[i]; | |
1160 | if (!strcmp(port_rec->name, br_int->name)) { | |
1161 | continue; | |
1162 | } | |
1163 | const char *chassis_id = smap_get(&port_rec->external_ids, | |
1164 | "ovn-chassis-id"); | |
f1a8bd06 | 1165 | if (chassis_id && !strcmp(chassis_id, chassis->name)) { |
0ee8aaf6 RR |
1166 | continue; |
1167 | } | |
1168 | const char *localnet = smap_get(&port_rec->external_ids, | |
1169 | "ovn-localnet-port"); | |
1170 | for (int j = 0; j < port_rec->n_interfaces; j++) { | |
1171 | const struct ovsrec_interface *iface_rec = port_rec->interfaces[j]; | |
1172 | if (!iface_rec->n_ofport) { | |
1173 | continue; | |
1174 | } | |
1175 | if (localnet) { | |
1176 | int64_t ofport = iface_rec->ofport[0]; | |
1177 | if (ofport < 1 || ofport > ofp_to_u16(OFPP_MAX)) { | |
1178 | continue; | |
1179 | } | |
1180 | simap_put(localnet_ofports, localnet, ofport); | |
1181 | continue; | |
1182 | } | |
1183 | const char *iface_id = smap_get(&iface_rec->external_ids, | |
1184 | "iface-id"); | |
1185 | if (!iface_id) { | |
1186 | continue; | |
1187 | } | |
1188 | const struct sbrec_port_binding *pb | |
1189 | = lport_lookup_by_name(lports, iface_id); | |
1190 | if (!pb) { | |
1191 | continue; | |
1192 | } | |
1193 | struct local_datapath *ld | |
1194 | = get_local_datapath(local_datapaths, | |
1195 | pb->datapath->tunnel_key); | |
1196 | if (ld && ld->localnet_port) { | |
1197 | sset_add(localnet_vifs, iface_id); | |
1198 | } | |
1199 | } | |
1200 | } | |
f1a8bd06 BP |
1201 | |
1202 | const struct local_datapath *ld; | |
1203 | HMAP_FOR_EACH (ld, hmap_node, local_datapaths) { | |
1204 | if (!ld->has_local_l3gateway) { | |
1205 | continue; | |
1206 | } | |
1207 | ||
1208 | for (size_t i = 0; i < ld->ldatapath->n_lports; i++) { | |
1209 | const struct sbrec_port_binding *pb = ld->ldatapath->lports[i]; | |
1210 | if (!strcmp(pb->type, "l3gateway") | |
1211 | /* && it's on this chassis */) { | |
1212 | sset_add(local_l3gw_ports, pb->logical_port); | |
1213 | } | |
1214 | } | |
1215 | } | |
0ee8aaf6 RR |
1216 | } |
1217 | ||
8439c2eb CSV |
1218 | static void |
1219 | get_nat_addresses_and_keys(struct sset *nat_address_keys, | |
1220 | struct sset *local_l3gw_ports, | |
1221 | const struct lport_index *lports, | |
1222 | struct shash *nat_addresses) | |
1223 | { | |
1224 | const char *gw_port; | |
1225 | SSET_FOR_EACH(gw_port, local_l3gw_ports) { | |
1226 | const struct sbrec_port_binding *pb = lport_lookup_by_name(lports, | |
1227 | gw_port); | |
1228 | if (!pb) { | |
1229 | continue; | |
1230 | } | |
1231 | const char *nat_addresses_options = smap_get(&pb->options, | |
1232 | "nat-addresses"); | |
1233 | if (!nat_addresses_options) { | |
1234 | continue; | |
1235 | } | |
1236 | ||
1237 | struct lport_addresses *laddrs = xmalloc(sizeof *laddrs); | |
1238 | if (!extract_lsp_addresses(nat_addresses_options, laddrs)) { | |
1239 | free(laddrs); | |
1240 | continue; | |
1241 | } | |
1242 | int i; | |
1243 | for (i = 0; i < laddrs->n_ipv4_addrs; i++) { | |
1244 | char *name = xasprintf("%s-%s", pb->logical_port, | |
1245 | laddrs->ipv4_addrs[i].addr_s); | |
1246 | sset_add(nat_address_keys, name); | |
1247 | free(name); | |
1248 | } | |
1249 | shash_add(nat_addresses, pb->logical_port, laddrs); | |
1250 | } | |
1251 | } | |
1252 | ||
0ee8aaf6 RR |
1253 | static void |
1254 | send_garp_wait(void) | |
1255 | { | |
1256 | poll_timer_wait_until(send_garp_time); | |
1257 | } | |
1258 | ||
1259 | static void | |
f1a8bd06 BP |
1260 | send_garp_run(const struct ovsrec_bridge *br_int, |
1261 | const struct sbrec_chassis *chassis, | |
0ee8aaf6 RR |
1262 | const struct lport_index *lports, |
1263 | struct hmap *local_datapaths) | |
1264 | { | |
1265 | struct sset localnet_vifs = SSET_INITIALIZER(&localnet_vifs); | |
8439c2eb CSV |
1266 | struct sset local_l3gw_ports = SSET_INITIALIZER(&local_l3gw_ports); |
1267 | struct sset nat_ip_keys = SSET_INITIALIZER(&nat_ip_keys); | |
0ee8aaf6 | 1268 | struct simap localnet_ofports = SIMAP_INITIALIZER(&localnet_ofports); |
8439c2eb | 1269 | struct shash nat_addresses; |
0ee8aaf6 | 1270 | |
8439c2eb | 1271 | shash_init(&nat_addresses); |
0ee8aaf6 | 1272 | |
f1a8bd06 | 1273 | get_localnet_vifs_l3gwports(br_int, chassis, lports, local_datapaths, |
8439c2eb CSV |
1274 | &localnet_vifs, &localnet_ofports, &local_l3gw_ports); |
1275 | ||
1276 | get_nat_addresses_and_keys(&nat_ip_keys, &local_l3gw_ports, lports, | |
1277 | &nat_addresses); | |
1278 | /* For deleted ports and deleted nat ips, remove from send_garp_data. */ | |
0ee8aaf6 RR |
1279 | struct shash_node *iter, *next; |
1280 | SHASH_FOR_EACH_SAFE (iter, next, &send_garp_data) { | |
8439c2eb CSV |
1281 | if (!sset_contains(&localnet_vifs, iter->name) && |
1282 | !sset_contains(&nat_ip_keys, iter->name)) { | |
0ee8aaf6 RR |
1283 | send_garp_delete(iter->name); |
1284 | } | |
1285 | } | |
1286 | ||
1287 | /* Update send_garp_data. */ | |
1288 | const char *iface_id; | |
1289 | SSET_FOR_EACH (iface_id, &localnet_vifs) { | |
1290 | const struct sbrec_port_binding *pb = lport_lookup_by_name(lports, | |
1291 | iface_id); | |
1292 | if (pb) { | |
8439c2eb CSV |
1293 | send_garp_update(pb, &localnet_ofports, local_datapaths, |
1294 | &nat_addresses); | |
1295 | } | |
1296 | } | |
1297 | ||
1298 | /* Update send_garp_data for nat-addresses. */ | |
1299 | const char *gw_port; | |
1300 | SSET_FOR_EACH (gw_port, &local_l3gw_ports) { | |
1301 | const struct sbrec_port_binding *pb = lport_lookup_by_name(lports, | |
1302 | gw_port); | |
1303 | if (pb) { | |
1304 | send_garp_update(pb, &localnet_ofports, local_datapaths, | |
1305 | &nat_addresses); | |
0ee8aaf6 RR |
1306 | } |
1307 | } | |
1308 | ||
1309 | /* Send GARPs, and update the next announcement. */ | |
1310 | long long int current_time = time_msec(); | |
1311 | send_garp_time = LLONG_MAX; | |
1312 | SHASH_FOR_EACH (iter, &send_garp_data) { | |
1313 | long long int next_announce = send_garp(iter->data, current_time); | |
1314 | if (send_garp_time > next_announce) { | |
1315 | send_garp_time = next_announce; | |
1316 | } | |
1317 | } | |
1318 | sset_destroy(&localnet_vifs); | |
8439c2eb | 1319 | sset_destroy(&local_l3gw_ports); |
0ee8aaf6 | 1320 | simap_destroy(&localnet_ofports); |
02d1f722 BP |
1321 | |
1322 | SHASH_FOR_EACH_SAFE (iter, next, &nat_addresses) { | |
1323 | struct lport_addresses *laddrs = iter->data; | |
1324 | destroy_lport_addresses(laddrs); | |
1325 | shash_delete(&nat_addresses, iter); | |
1326 | free(laddrs); | |
1327 | } | |
1328 | shash_destroy(&nat_addresses); | |
1329 | ||
96a234ad | 1330 | sset_destroy(&nat_ip_keys); |
0ee8aaf6 | 1331 | } |
e75451fe ZKL |
1332 | |
1333 | static void | |
1334 | reload_metadata(struct ofpbuf *ofpacts, const struct match *md) | |
1335 | { | |
1336 | enum mf_field_id md_fields[] = { | |
847b8b02 | 1337 | #if FLOW_N_REGS == 16 |
e75451fe ZKL |
1338 | MFF_REG0, |
1339 | MFF_REG1, | |
1340 | MFF_REG2, | |
1341 | MFF_REG3, | |
1342 | MFF_REG4, | |
1343 | MFF_REG5, | |
1344 | MFF_REG6, | |
1345 | MFF_REG7, | |
847b8b02 JP |
1346 | MFF_REG8, |
1347 | MFF_REG9, | |
1348 | MFF_REG10, | |
1349 | MFF_REG11, | |
1350 | MFF_REG12, | |
1351 | MFF_REG13, | |
1352 | MFF_REG14, | |
1353 | MFF_REG15, | |
e75451fe ZKL |
1354 | #else |
1355 | #error | |
1356 | #endif | |
1357 | MFF_METADATA, | |
1358 | }; | |
1359 | for (size_t i = 0; i < ARRAY_SIZE(md_fields); i++) { | |
1360 | const struct mf_field *field = mf_from_id(md_fields[i]); | |
1361 | if (!mf_is_all_wild(field, &md->wc)) { | |
128684a6 JR |
1362 | union mf_value value; |
1363 | mf_get_value(field, &md->flow, &value); | |
1364 | ofpact_put_set_field(ofpacts, field, &value, NULL); | |
e75451fe ZKL |
1365 | } |
1366 | } | |
1367 | } | |
1368 | ||
1369 | static void | |
f8a8db39 JP |
1370 | pinctrl_handle_nd_na(const struct flow *ip_flow, const struct match *md, |
1371 | struct ofpbuf *userdata) | |
e75451fe ZKL |
1372 | { |
1373 | /* This action only works for IPv6 ND packets, and the switch should only | |
1374 | * send us ND packets this way, but check here just to be sure. */ | |
1375 | if (!is_nd(ip_flow, NULL)) { | |
1376 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); | |
1377 | VLOG_WARN_RL(&rl, "NA action on non-ND packet"); | |
1378 | return; | |
1379 | } | |
1380 | ||
1381 | enum ofp_version version = rconn_get_version(swconn); | |
1382 | enum ofputil_protocol proto = ofputil_protocol_from_ofp_version(version); | |
1383 | ||
1384 | uint64_t packet_stub[128 / 8]; | |
1385 | struct dp_packet packet; | |
1386 | dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub); | |
1387 | ||
fa7f915c JP |
1388 | /* xxx These flags are not exactly correct. Look at section 7.2.4 |
1389 | * xxx of RFC 4861. For example, we need to set ND_RSO_ROUTER for | |
1390 | * xxx router's interfaces and ND_RSO_SOLICITED only if it was | |
1391 | * xxx requested. */ | |
16187903 JP |
1392 | compose_nd_na(&packet, ip_flow->dl_dst, ip_flow->dl_src, |
1393 | &ip_flow->nd_target, &ip_flow->ipv6_src, | |
1394 | htonl(ND_RSO_SOLICITED | ND_RSO_OVERRIDE)); | |
e75451fe ZKL |
1395 | |
1396 | /* Reload previous packet metadata. */ | |
1397 | uint64_t ofpacts_stub[4096 / 8]; | |
1398 | struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub); | |
1399 | reload_metadata(&ofpacts, md); | |
1400 | ||
1401 | enum ofperr error = ofpacts_pull_openflow_actions(userdata, userdata->size, | |
5c7c16d8 YHW |
1402 | version, NULL, NULL, |
1403 | &ofpacts); | |
e75451fe ZKL |
1404 | if (error) { |
1405 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); | |
1406 | VLOG_WARN_RL(&rl, "failed to parse actions for 'na' (%s)", | |
1407 | ofperr_to_string(error)); | |
1408 | goto exit; | |
1409 | } | |
1410 | ||
1411 | struct ofputil_packet_out po = { | |
1412 | .packet = dp_packet_data(&packet), | |
1413 | .packet_len = dp_packet_size(&packet), | |
1414 | .buffer_id = UINT32_MAX, | |
1415 | .in_port = OFPP_CONTROLLER, | |
1416 | .ofpacts = ofpacts.data, | |
1417 | .ofpacts_len = ofpacts.size, | |
1418 | }; | |
1419 | ||
1420 | queue_msg(ofputil_encode_packet_out(&po, proto)); | |
1421 | ||
1422 | exit: | |
1423 | dp_packet_uninit(&packet); | |
1424 | ofpbuf_uninit(&ofpacts); | |
1425 | } |