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