blob: 08e10b3cf58429d38387a032319f9875bb4d866a [file] [log] [blame]
rjw1f884582022-01-06 17:20:42 +08001/*
2 * Copyright (c) 2014 Chris Anderson
3 * Copyright (c) 2014 Brian Swetland
4 * Copyright (c) 2015 Nathaniel Quillin
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files
8 * (the "Software"), to deal in the Software without restriction,
9 * including without limitation the rights to use, copy, modify, merge,
10 * publish, distribute, sublicense, and/or sell copies of the Software,
11 * and to permit persons to whom the Software is furnished to do so,
12 * subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
21 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 */
25
26#include "minip-internal.h"
27
28#include <err.h>
29#include <errno.h>
30#include <iovec.h>
31#include <list.h>
32#include <malloc.h>
33#include <stdint.h>
34#include <trace.h>
35
36#define LOCAL_TRACE 0
37
38static struct list_node udp_list = LIST_INITIAL_VALUE(udp_list);
39
40struct udp_listener {
41 struct list_node list;
42 uint16_t port;
43 udp_callback_t callback;
44 void *arg;
45};
46
47typedef struct udp_socket {
48 uint32_t host;
49 uint16_t sport;
50 uint16_t dport;
51 const uint8_t *mac;
52} udp_socket_t;
53
54typedef struct udp_hdr {
55 uint16_t src_port;
56 uint16_t dst_port;
57 uint16_t len;
58 uint16_t chksum;
59} __PACKED udp_hdr_t;
60
61
62int udp_listen(uint16_t port, udp_callback_t cb, void *arg) {
63 struct udp_listener *entry, *temp;
64
65 list_for_every_entry_safe(&udp_list, entry, temp, struct udp_listener, list) {
66 if (entry->port == port) {
67 if (cb == NULL) {
68 list_delete(&entry->list);
69 return 0;
70 }
71 return -1;
72 }
73 }
74
75 if ((entry = malloc(sizeof(struct udp_listener))) == NULL) {
76 return -1;
77 }
78
79 entry->port = port;
80 entry->callback = cb;
81 entry->arg = arg;
82
83 list_add_tail(&udp_list, &entry->list);
84
85 return 0;
86}
87
88status_t udp_open(uint32_t host, uint16_t sport, uint16_t dport, udp_socket_t **handle)
89{
90 LTRACEF("host %u.%u.%u.%u sport %u dport %u handle %p\n",
91 IPV4_SPLIT(host), sport, dport, handle);
92 udp_socket_t *socket;
93 const uint8_t *dst_mac;
94
95 if (handle == NULL) {
96 return -EINVAL;
97 }
98
99 socket = (udp_socket_t *) malloc(sizeof(udp_socket_t));
100 if (!socket) {
101 return -ENOMEM;
102 }
103
104 dst_mac = arp_get_dest_mac(host);
105 if (dst_mac == NULL) {
106 free(socket);
107 return -EHOSTUNREACH;
108 }
109
110 socket->host = host;
111 socket->sport = sport;
112 socket->dport = dport;
113 socket->mac = dst_mac;
114
115 *handle = socket;
116
117 return NO_ERROR;
118}
119
120status_t udp_close(udp_socket_t *handle)
121{
122 if (handle == NULL) {
123 return -EINVAL;
124 }
125
126 free(handle);
127 return NO_ERROR;
128}
129
130status_t udp_send_iovec(const iovec_t *iov, uint iov_count, udp_socket_t *handle)
131{
132 pktbuf_t *p;
133 struct eth_hdr *eth;
134 struct ipv4_hdr *ip;
135 udp_hdr_t *udp;
136 status_t ret = NO_ERROR;
137 void *buf;
138 ssize_t len;
139
140 if (handle == NULL || iov == NULL || iov_count == 0) {
141 return -EINVAL;
142 }
143
144 if ((p = pktbuf_alloc()) == NULL) {
145 return -ENOMEM;
146 }
147
148 len = iovec_size(iov, iov_count);
149
150 buf = pktbuf_append(p, len);
151 udp = pktbuf_prepend(p, sizeof(udp_hdr_t));
152 ip = pktbuf_prepend(p, sizeof(struct ipv4_hdr));
153 eth = pktbuf_prepend(p, sizeof(struct eth_hdr));
154
155 iovec_to_membuf(buf, len, iov, iov_count, 0);
156
157 udp->src_port = htons(handle->sport);
158 udp->dst_port = htons(handle->dport);
159 udp->len = htons(sizeof(udp_hdr_t) + len);
160 udp->chksum = 0;
161
162 minip_build_mac_hdr(eth, handle->mac, ETH_TYPE_IPV4);
163 minip_build_ipv4_hdr(ip, handle->host, IP_PROTO_UDP, len + sizeof(udp_hdr_t));
164
165#if (MINIP_USE_UDP_CHECKSUM != 0)
166 udp->chksum = rfc768_chksum(ip, udp);
167#endif
168
169 minip_tx_handler(p);
170
171 return ret;
172}
173
174status_t udp_send(void *buf, size_t len, udp_socket_t *handle)
175{
176 iovec_t iov;
177
178 LTRACEF("buf %p, len %zu, handle %p\n", buf, len, handle);
179
180 if (buf == NULL || len == 0) {
181 return -EINVAL;
182 }
183
184 iov.iov_base = buf;
185 iov.iov_len = len;
186
187 return udp_send_iovec(&iov, 1, handle);
188}
189
190void udp_input(pktbuf_t *p, uint32_t src_ip)
191{
192 udp_hdr_t *udp;
193 struct udp_listener *e;
194 uint16_t port;
195
196 if ((udp = pktbuf_consume(p, sizeof(udp_hdr_t))) == NULL) {
197 return;
198 }
199
200 port = ntohs(udp->dst_port);
201
202 list_for_every_entry(&udp_list, e, struct udp_listener, list) {
203 if (e->port == port) {
204 e->callback(p->data, p->dlen, src_ip, ntohs(udp->src_port), e->arg);
205 return;
206 }
207 }
208}