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                            &eth->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(&eth->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