1 /*
2  * Copyright (c) 2014 Chris Anderson
3  * Copyright (c) 2014 Brian Swetland
4  * Copyright (c) 2015 Nathaniel Quillin
5  *
6  * Use of this source code is governed by a MIT-style
7  * license that can be found in the LICENSE file or at
8  * https://opensource.org/licenses/MIT
9  */
10 
11 #include "minip-internal.h"
12 
13 #include <lk/err.h>
14 #include <errno.h>
15 #include <iovec.h>
16 #include <lk/list.h>
17 #include <malloc.h>
18 #include <stdint.h>
19 #include <lk/trace.h>
20 
21 #define LOCAL_TRACE 0
22 
23 static struct list_node udp_list = LIST_INITIAL_VALUE(udp_list);
24 
25 struct udp_listener {
26     struct list_node list;
27     uint16_t port;
28     udp_callback_t callback;
29     void *arg;
30 };
31 
32 typedef struct udp_socket {
33     uint32_t host;
34     uint16_t sport;
35     uint16_t dport;
36     const uint8_t *mac;
37 } udp_socket_t;
38 
39 typedef struct udp_hdr {
40     uint16_t src_port;
41     uint16_t dst_port;
42     uint16_t len;
43     uint16_t chksum;
44 } __PACKED udp_hdr_t;
45 
46 
udp_listen(uint16_t port,udp_callback_t cb,void * arg)47 int udp_listen(uint16_t port, udp_callback_t cb, void *arg) {
48     struct udp_listener *entry, *temp;
49 
50     list_for_every_entry_safe(&udp_list, entry, temp, struct udp_listener, list) {
51         if (entry->port == port) {
52             if (cb == NULL) {
53                 list_delete(&entry->list);
54                 return 0;
55             }
56             return -1;
57         }
58     }
59 
60     if ((entry = malloc(sizeof(struct udp_listener))) == NULL) {
61         return -1;
62     }
63 
64     entry->port = port;
65     entry->callback = cb;
66     entry->arg = arg;
67 
68     list_add_tail(&udp_list, &entry->list);
69 
70     return 0;
71 }
72 
udp_open(uint32_t host,uint16_t sport,uint16_t dport,udp_socket_t ** handle)73 status_t udp_open(uint32_t host, uint16_t sport, uint16_t dport, udp_socket_t **handle) {
74     LTRACEF("host %u.%u.%u.%u sport %u dport %u handle %p\n",
75             IPV4_SPLIT(host), sport, dport, handle);
76     udp_socket_t *socket;
77     const uint8_t *dst_mac;
78 
79     if (handle == NULL) {
80         return -EINVAL;
81     }
82 
83     socket = (udp_socket_t *) malloc(sizeof(udp_socket_t));
84     if (!socket) {
85         return -ENOMEM;
86     }
87 
88     dst_mac = arp_get_dest_mac(host);
89     if (dst_mac == NULL) {
90         free(socket);
91         return -EHOSTUNREACH;
92     }
93 
94     socket->host = host;
95     socket->sport = sport;
96     socket->dport = dport;
97     socket->mac = dst_mac;
98 
99     *handle = socket;
100 
101     return NO_ERROR;
102 }
103 
udp_close(udp_socket_t * handle)104 status_t udp_close(udp_socket_t *handle) {
105     if (handle == NULL) {
106         return -EINVAL;
107     }
108 
109     free(handle);
110     return NO_ERROR;
111 }
112 
udp_send_iovec(const iovec_t * iov,uint iov_count,udp_socket_t * handle)113 status_t udp_send_iovec(const iovec_t *iov, uint iov_count, udp_socket_t *handle) {
114     pktbuf_t *p;
115     struct eth_hdr *eth;
116     struct ipv4_hdr *ip;
117     udp_hdr_t *udp;
118     status_t ret = NO_ERROR;
119     void *buf;
120     ssize_t len;
121 
122     if (handle == NULL || iov == NULL || iov_count == 0) {
123         return -EINVAL;
124     }
125 
126     if ((p = pktbuf_alloc()) == NULL) {
127         return -ENOMEM;
128     }
129 
130     len = iovec_size(iov, iov_count);
131 
132     buf = pktbuf_append(p, len);
133     udp = pktbuf_prepend(p, sizeof(udp_hdr_t));
134     ip = pktbuf_prepend(p, sizeof(struct ipv4_hdr));
135     eth = pktbuf_prepend(p, sizeof(struct eth_hdr));
136 
137     iovec_to_membuf(buf, len, iov, iov_count, 0);
138 
139     udp->src_port   = htons(handle->sport);
140     udp->dst_port   = htons(handle->dport);
141     udp->len        = htons(sizeof(udp_hdr_t) + len);
142     udp->chksum     = 0;
143 
144     minip_build_mac_hdr(eth, handle->mac, ETH_TYPE_IPV4);
145     minip_build_ipv4_hdr(ip, handle->host, IP_PROTO_UDP, len + sizeof(udp_hdr_t));
146 
147 #if (MINIP_USE_UDP_CHECKSUM != 0)
148     udp->chksum = rfc768_chksum(ip, udp);
149 #endif
150 
151     minip_tx_handler(p);
152 
153     return ret;
154 }
155 
udp_send(void * buf,size_t len,udp_socket_t * handle)156 status_t udp_send(void *buf, size_t len, udp_socket_t *handle) {
157     iovec_t iov;
158 
159     LTRACEF("buf %p, len %zu, handle %p\n", buf, len, handle);
160 
161     if (buf == NULL || len == 0) {
162         return -EINVAL;
163     }
164 
165     iov.iov_base = buf;
166     iov.iov_len = len;
167 
168     return udp_send_iovec(&iov, 1, handle);
169 }
170 
udp_input(pktbuf_t * p,uint32_t src_ip)171 void udp_input(pktbuf_t *p, uint32_t src_ip) {
172     udp_hdr_t *udp;
173     struct udp_listener *e;
174     uint16_t port;
175 
176     if ((udp = pktbuf_consume(p, sizeof(udp_hdr_t))) == NULL) {
177         return;
178     }
179 
180     port = ntohs(udp->dst_port);
181 
182     list_for_every_entry(&udp_list, e, struct udp_listener, list) {
183         if (e->port == port) {
184             e->callback(p->data, p->dlen, src_ip, ntohs(udp->src_port), e->arg);
185             return;
186         }
187     }
188 }
189