1 /*
2 * Copyright (c) 2014, Cisco Systems, Inc. All rights reserved.
3 *
4 * LICENSE_BEGIN
5 *
6 * This software is available to you under a choice of one of two
7 * licenses. You may choose to be licensed under the terms of the GNU
8 * General Public License (GPL) Version 2, available from the file
9 * COPYING in the main directory of this source tree, or the
10 * BSD license below:
11 *
12 * Redistribution and use in source and binary forms, with or
13 * without modification, are permitted provided that the following
14 * conditions are met:
15 *
16 * - Redistributions of source code must retain the above
17 * copyright notice, this list of conditions and the following
18 * disclaimer.
19 *
20 * - Redistributions in binary form must reproduce the above
21 * copyright notice, this list of conditions and the following
22 * disclaimer in the documentation and/or other materials
23 * provided with the distribution.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
28 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
29 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
30 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
31 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
32 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
33 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
35 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 *
38 * LICENSE_END
39 *
40 *
41 */
42
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <unistd.h>
46 #include <string.h>
47 #include <fcntl.h>
48 #include <dirent.h>
49 #include <pthread.h>
50 #include <errno.h>
51 #include <sys/stat.h>
52 #include <arpa/inet.h>
53
54 #include "usnic_ip_utils.h"
55 #include "libnl_utils.h"
56
57 #include "usnic_direct.h"
58 #include "usd.h"
59 #include "usd_queue.h"
60 #include "usd_time.h"
61 #include "usd_dest.h"
62 #include "usd_socket.h"
63
64 extern TAILQ_HEAD(, usd_device) usd_device_list;
65
66 static struct usd_dest_params usd_dest_params = {
67 .dp_arp_timeout = 1000,
68 .dp_max_arps = 10
69 };
70
71 int
usd_get_dest_distance(struct usd_device * dev,uint32_t daddr_be,int * metric_o)72 usd_get_dest_distance(
73 struct usd_device *dev,
74 uint32_t daddr_be,
75 int *metric_o)
76 {
77 uint32_t nh_ip_addr;
78 int ret;
79
80 ret = usnic_nl_rt_lookup(dev->ud_attrs.uda_ipaddr_be, daddr_be,
81 dev->ud_attrs.uda_ifindex, &nh_ip_addr);
82 if (ret != 0) {
83 *metric_o = -1;
84 ret = 0;
85 } else if (nh_ip_addr == 0) {
86 *metric_o = 0;
87 } else {
88 *metric_o = 1;
89 }
90
91 return ret;
92 }
93
94 static void
usd_dest_set_complete(struct usd_device * dev,struct usd_dest_req * req)95 usd_dest_set_complete(
96 struct usd_device *dev,
97 struct usd_dest_req *req)
98 {
99 req->udr_complete = 1;
100 if (req->udr_status != 0 && req->udr_dest != NULL) {
101 free(req->udr_dest);
102 req->udr_dest = NULL;
103 }
104 TAILQ_REMOVE(&dev->ud_pending_reqs, req, udr_link);
105 TAILQ_INSERT_TAIL(&dev->ud_completed_reqs, req, udr_link);
106 }
107
108 static int
usd_dest_trigger_arp(struct usd_device * dev,struct usd_dest_req * req)109 usd_dest_trigger_arp(
110 struct usd_device *dev,
111 struct usd_dest_req *req)
112 {
113 int ret;
114
115 usd_get_time(&req->udr_last_arp);
116 req->udr_arps_sent++;
117
118 ret =
119 usnic_arp_request(req->udr_daddr_be, dev->ud_arp_sockfd);
120 return ret;
121 }
122
123 static int
usd_check_dest_resolved(struct usd_device * dev,struct usd_dest_req * req)124 usd_check_dest_resolved(
125 struct usd_device *dev,
126 struct usd_dest_req *req)
127 {
128 struct ether_header *eth;
129 int ret;
130
131 eth = &req->udr_dest->ds_dest.ds_udp.u_hdr.uh_eth;
132 ret = usnic_arp_lookup(dev->ud_attrs.uda_ifname,
133 req->udr_daddr_be, dev->ud_arp_sockfd,
134 ð->ether_dhost[0]);
135
136 if (ret == EAGAIN)
137 return -EAGAIN;
138
139 /* for better or worse, resolution is complete */
140 req->udr_status = -ret;
141 return 0;
142 }
143
144 /*
145 * Loop through the ordered pending create_dest request queue.
146 * If an entry is complete, move it to the completed queue.
147 * If the retry timeout for an entry has arrived, re-trigger the ARP
148 */
149 static void
usd_dest_progress_dev(struct usd_device * dev)150 usd_dest_progress_dev(
151 struct usd_device *dev)
152 {
153 struct usd_dest_req *req;
154 struct usd_dest_req *tmpreq;
155 usd_time_t now;
156 int delta;
157 int ret;
158
159 usd_get_time(&now);
160
161 TAILQ_FOREACH_SAFE(req, tmpreq, &dev->ud_pending_reqs, udr_link) {
162
163 /* resolution complete? */
164 ret = usd_check_dest_resolved(dev, req);
165 if (ret == 0) {
166 usd_dest_set_complete(dev, req);
167 continue;
168 }
169
170
171 /* time for next ARP trigger? */
172 delta = usd_time_diff(req->udr_last_arp, now);
173 if (delta > (int) usd_dest_params.dp_arp_timeout) {
174 if (req->udr_arps_sent >= usd_dest_params.dp_max_arps) {
175 req->udr_status = -EHOSTUNREACH;
176 usd_dest_set_complete(dev, req);
177 continue;
178 }
179
180 ret = usd_dest_trigger_arp(dev, req);
181 if (ret != 0) {
182 req->udr_status = ret;
183 usd_dest_set_complete(dev, req);
184 }
185 }
186 }
187 }
188
189 static void
usd_dest_progress(void)190 usd_dest_progress(void)
191 {
192 struct usd_device *dev;
193
194 TAILQ_FOREACH(dev, &usd_device_list, ud_link) {
195 usd_dest_progress_dev(dev);
196 }
197 }
198
199 /*
200 * Fill in all of a header except the dest MAC and the UDP ports
201 * specified remote host
202 */
203 void
usd_fill_udp_dest(struct usd_dest * dest,struct usd_device_attrs * dap,uint32_t daddr_be,uint16_t dport_be)204 usd_fill_udp_dest(
205 struct usd_dest *dest,
206 struct usd_device_attrs *dap,
207 uint32_t daddr_be,
208 uint16_t dport_be)
209 {
210 struct ether_header eth = {
211 .ether_type = htons(0x0800)
212 };
213
214 struct udphdr udp = {
215 .dest = dport_be
216 };
217
218 struct iphdr ip = {
219 .saddr = dap->uda_ipaddr_be,
220 .daddr = daddr_be,
221 .protocol = IPPROTO_UDP,
222 .version = 4,
223 .frag_off = 0,
224 .ihl = 5, /* no options */
225 .tos = 0,
226 .ttl = 8
227 };
228
229 /* Workaround taking a pointer to an element of a packed structure due to
230 * warnings in Clang 4.0.1 and beyond.
231 */
232 memcpy(eth.ether_shost, dap->uda_mac_addr, ETH_ALEN);
233 dest->ds_dest.ds_udp.u_hdr.uh_eth = eth;
234 dest->ds_dest.ds_udp.u_hdr.uh_udp = udp;
235 dest->ds_dest.ds_udp.u_hdr.uh_ip = ip;
236 }
237
238 static int
usd_create_udp_dest_start(struct usd_device * dev,uint32_t daddr_be,uint16_t dport_be,struct usd_dest_req ** req_o)239 usd_create_udp_dest_start(
240 struct usd_device *dev,
241 uint32_t daddr_be,
242 uint16_t dport_be,
243 struct usd_dest_req **req_o)
244 {
245 struct usd_dest_req *req;
246 struct usd_dest *dest;
247 uint32_t first_hop_daddr_be;
248 int ret;
249
250 /* catch a mistake that will almost always lead to hung programs */
251 if (daddr_be == 0 || dport_be == 0) {
252 return -EINVAL;
253 }
254
255 req = calloc(sizeof(*req), 1);
256 dest = calloc(sizeof(*dest), 1);
257 if (req == NULL || dest == NULL) {
258 ret = -errno;
259 goto fail;
260 }
261
262 ret = usnic_nl_rt_lookup(dev->ud_attrs.uda_ipaddr_be,
263 daddr_be, dev->ud_attrs.uda_ifindex,
264 &first_hop_daddr_be);
265 if (ret != 0) {
266 /* EHOSTUNREACH is non-fatal, but we are done with resolution */
267 if (ret == EHOSTUNREACH) {
268 req->udr_status = -EHOSTUNREACH;
269 free(dest);
270 goto complete;
271 } else {
272 ret = -ret;
273 }
274 goto fail;
275 }
276 if (first_hop_daddr_be == 0)
277 first_hop_daddr_be = daddr_be;
278
279 /* Fill in dest as much as we can */
280 usd_fill_udp_dest(dest, &dev->ud_attrs, daddr_be, dport_be);
281
282 /* initiate request and add to tail of pending list */
283 req->udr_daddr_be = first_hop_daddr_be;
284 req->udr_dest = dest;
285
286 ret = usd_dest_trigger_arp(dev, req);
287 if (ret != 0)
288 goto fail;
289
290 complete:
291 TAILQ_INSERT_TAIL(&dev->ud_pending_reqs, req, udr_link);
292 if (req->udr_status != 0) {
293 usd_dest_set_complete(dev, req);
294 }
295 *req_o = req;
296
297 return 0;
298
299 fail:
300 if (req != NULL)
301 free(req);
302 if (dest != NULL)
303 free(dest);
304 return ret;
305 }
306
307
308 /*
309 * synchronously create a UDP destination by initiating the
310 * resolution, then waiting for it to complete
311 */
312 static int
usd_create_udp_dest(struct usd_device * dev,uint32_t daddr_be,uint16_t dport_be,struct usd_dest ** dest_o)313 usd_create_udp_dest(
314 struct usd_device *dev,
315 uint32_t daddr_be,
316 uint16_t dport_be,
317 struct usd_dest **dest_o)
318 {
319 struct usd_dest_req *req;
320 int ret;
321
322 ret = usd_create_udp_dest_start(dev, daddr_be, dport_be, &req);
323 if (ret != 0)
324 return ret;
325
326 /* loop until request completes or times out */
327 while (req->udr_complete == 0) {
328 usd_dest_progress();
329 }
330
331 ret = req->udr_status;
332 if (ret == 0)
333 *dest_o = req->udr_dest;
334
335 TAILQ_REMOVE(&dev->ud_completed_reqs, req, udr_link);
336 free(req);
337 return ret;
338 }
339
340 /*
341 * Build and save a IP header appropriate for sending to the
342 * specified remote host
343 */
344 int
usd_create_ip_dest(struct usd_device * dev,uint32_t daddr_be,struct usd_dest ** dest_o)345 usd_create_ip_dest(
346 struct usd_device *dev,
347 uint32_t daddr_be,
348 struct usd_dest **dest_o)
349 {
350 int ret;
351
352 ret = usd_create_udp_dest(dev, daddr_be, 0, dest_o);
353 return ret;
354 }
355
356 void
usd_dest_set_udp_ports(struct usd_dest * dest,struct usd_qp * src_uqp,uint16_t dest_port_be)357 usd_dest_set_udp_ports(
358 struct usd_dest *dest,
359 struct usd_qp *src_uqp,
360 uint16_t dest_port_be)
361 {
362 struct usd_qp_impl *qp = to_qpi(src_uqp);
363 struct udphdr udp = {
364 .source = qp->uq_attrs.uqa_local_addr.ul_addr.ul_udp.u_addr.sin_port,
365 .dest = dest_port_be
366 };
367
368 /* Workaround taking a pointer to an element of a packed structure due to
369 * warnings in Clang 4.0.1 and beyond.
370 */
371 dest->ds_dest.ds_udp.u_hdr.uh_udp = udp;
372 }
373
374 /*
375 * Synchronously creates a destination
376 */
377 int
usd_create_dest(struct usd_device * dev,uint32_t daddr_be,uint16_t dport_be,struct usd_dest ** dest_o)378 usd_create_dest(
379 struct usd_device *dev,
380 uint32_t daddr_be,
381 uint16_t dport_be,
382 struct usd_dest **dest_o)
383 {
384 int ret;
385
386 ret = usd_create_udp_dest(dev, daddr_be, dport_be, dest_o);
387
388 return ret;
389 }
390
391 int
usd_destroy_dest(struct usd_dest * dest)392 usd_destroy_dest(
393 struct usd_dest *dest)
394 {
395 if (dest != NULL) {
396 free(dest);
397 }
398 return 0;
399 }
400
401 /*
402 * Get address resolution settings
403 */
404 int
usd_get_dest_params(struct usd_dest_params * params)405 usd_get_dest_params(
406 struct usd_dest_params *params)
407 {
408 if (params == NULL)
409 return -EINVAL;
410
411 *params = usd_dest_params;
412 return 0;
413 }
414
415 /*
416 * Set address resolution settings
417 * Settings may not be changed while any resolution requests are in progress.
418 */
419 int
usd_set_dest_params(struct usd_dest_params * params)420 usd_set_dest_params(
421 struct usd_dest_params *params)
422 {
423 if (params == NULL)
424 return -EINVAL;
425
426 /* blindly set parameters, allowing user to shoot self if desired */
427 usd_dest_params.dp_arp_timeout = params->dp_arp_timeout;
428 usd_dest_params.dp_max_arps = params->dp_max_arps;
429
430 return 0;
431 }
432
433 /*
434 * Start destination creation
435 * Resolution progress is performed in usd_create_dest_query() and
436 * usd_create_dest_poll()
437 */
438 int
usd_create_dest_start(struct usd_device * dev,uint32_t daddr_be,uint16_t dport_be,void * context)439 usd_create_dest_start(
440 struct usd_device *dev,
441 uint32_t daddr_be,
442 uint16_t dport_be,
443 void *context)
444 {
445 struct usd_dest_req *req;
446 int ret;
447
448 req = NULL;
449 ret = usd_create_udp_dest_start(dev, daddr_be, dport_be, &req);
450
451 if (ret == 0) {
452 req->udr_context = context;
453 }
454
455 return ret;
456 }
457
458 /*
459 * Return first completed destinatin request
460 */
461 int
usd_create_dest_poll(struct usd_device * dev,void ** context_o,int * status,struct usd_dest ** dest_o)462 usd_create_dest_poll(
463 struct usd_device *dev,
464 void **context_o,
465 int *status,
466 struct usd_dest **dest_o)
467 {
468 struct usd_dest_req *req;
469
470 usd_dest_progress();
471
472 if (!TAILQ_EMPTY(&dev->ud_completed_reqs)) {
473 req = TAILQ_FIRST(&dev->ud_completed_reqs);
474 TAILQ_REMOVE(&dev->ud_completed_reqs, req, udr_link);
475 *context_o = req->udr_context;
476 *status = req->udr_status;
477 if (*status == 0)
478 *dest_o = req->udr_dest;
479 free(req);
480 return 0;
481
482 } else {
483 return -EAGAIN;
484 }
485 }
486
487 /*
488 * Check completion of a particular request
489 */
490 int
usd_create_dest_query(struct usd_device * dev,void * context,int * status,struct usd_dest ** dest_o)491 usd_create_dest_query(
492 struct usd_device *dev,
493 void *context,
494 int *status,
495 struct usd_dest **dest_o)
496 {
497 struct usd_dest_req *req;
498
499 usd_dest_progress();
500
501 TAILQ_FOREACH(req, &dev->ud_completed_reqs, udr_link) {
502 if (req->udr_context == context) {
503 TAILQ_REMOVE(&dev->ud_completed_reqs, req, udr_link);
504 *status = req->udr_status;
505 if (*status == 0)
506 *dest_o = req->udr_dest;
507 free(req);
508 return 0;
509 }
510 }
511
512 return -EAGAIN;
513 }
514
515 /*
516 * Cancel a destination creation in progress
517 * Look through both the pending and completed queues, simply
518 * squash the record if we find it.
519 */
520 int
usd_create_dest_cancel(struct usd_device * dev,void * context)521 usd_create_dest_cancel(
522 struct usd_device *dev,
523 void *context)
524 {
525 struct usd_dest_req *req;
526
527 TAILQ_FOREACH(req, &dev->ud_pending_reqs, udr_link) {
528 if (req->udr_context == context) {
529 TAILQ_REMOVE(&dev->ud_pending_reqs, req, udr_link);
530 goto found;
531 }
532 }
533
534 TAILQ_FOREACH(req, &dev->ud_completed_reqs, udr_link) {
535 if (req->udr_context == context) {
536 TAILQ_REMOVE(&dev->ud_completed_reqs, req, udr_link);
537 goto found;
538 }
539 }
540
541 return -EINVAL;
542
543 found:
544 free(req->udr_dest);
545 free(req);
546 return 0;
547 }
548
549 /*
550 * Create a destination given a MAC address
551 */
552 int
usd_create_dest_with_mac(struct usd_device * dev,uint32_t daddr_be,uint16_t dport_be,uint8_t * dmac,struct usd_dest ** dest_o)553 usd_create_dest_with_mac(
554 struct usd_device *dev,
555 uint32_t daddr_be,
556 uint16_t dport_be,
557 uint8_t * dmac,
558 struct usd_dest **dest_o)
559 {
560 struct ether_header *eth;
561 struct usd_dest *dest;
562
563 dest = calloc(sizeof(*dest), 1);
564 if (dest == NULL)
565 return -errno;
566
567 /* Fill in dest as much as we can */
568 usd_fill_udp_dest(dest, &dev->ud_attrs, daddr_be, dport_be);
569
570 /* copy in MAC from caller */
571 eth = &dest->ds_dest.ds_udp.u_hdr.uh_eth;
572 memcpy(ð->ether_dhost[0], dmac, ETH_ALEN);
573
574 *dest_o = dest;
575 return 0;
576 }
577
578 /*
579 * Expand a destination
580 */
581 int
usd_expand_dest(struct usd_dest * dest,uint32_t * ip_be_o,uint16_t * port_be_o)582 usd_expand_dest(
583 struct usd_dest *dest,
584 uint32_t *ip_be_o,
585 uint16_t *port_be_o)
586 {
587 if (ip_be_o != NULL) {
588 *ip_be_o = dest->ds_dest.ds_udp.u_hdr.uh_ip.daddr;
589 }
590 if (port_be_o != NULL) {
591 *port_be_o = dest->ds_dest.ds_udp.u_hdr.uh_udp.dest;
592 }
593
594 return 0;
595 }
596