1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (C) 2022 Felix Fietkau <nbd@nbd.name>
6 #include <sys/socket.h>
11 #include <netinet/in.h>
12 #include <netinet/ip.h>
13 #include <netinet/ip6.h>
14 #include <netinet/udp.h>
16 #include <libubox/utils.h>
20 int network_get_endpoint(union network_endpoint
*dest
, int af
, const char *str
,
21 int default_port
, int idx
)
23 struct addrinfo hints
= {
24 .ai_flags
= AI_ADDRCONFIG
,
27 char *buf
= strdup(str
);
28 char *host
= buf
, *port
;
29 struct addrinfo
*ai
, *ai_cur
;
33 memset(dest
, 0, sizeof(*dest
));
40 port
= strchr(host
, ']');
47 else if (*port
== ':')
51 hints
.ai_family
= AF_INET6
;
52 hints
.ai_flags
|= AI_NUMERICHOST
;
56 port
= strchr(host
, ':');
61 if (getaddrinfo(host
, port
, &hints
, &ai
) || !ai
)
66 for (n_res
= 0; ai_cur
; ai_cur
= ai_cur
->ai_next
, n_res
++)
74 if (ai_cur
->ai_addrlen
> sizeof(*dest
))
77 memcpy(dest
, ai_cur
->ai_addr
, ai_cur
->ai_addrlen
);
79 dest
->in
.sin_port
= htons(default_port
);
90 int network_get_subnet(int af
, union network_addr
*addr
, int *mask
, const char *str
)
92 char *buf
= strdup(str
);
101 sep
= strchr(buf
, '/');
107 val
= strtoul(sep
, &end
, 0);
108 if ((end
&& *end
) || val
> *mask
)
114 if (inet_pton(af
, buf
, addr
) == 1)
122 int network_get_local_addr(void *local
, const union network_endpoint
*target
)
124 union network_endpoint ep
= {};
129 memset(local
, 0, sizeof(union network_addr
));
130 if (target
->sa
.sa_family
== AF_INET6
)
131 len
= sizeof(ep
.in6
);
135 fd
= socket(target
->sa
.sa_family
, SOCK_DGRAM
, IPPROTO_UDP
);
139 if (connect(fd
, (const struct sockaddr
*)target
, len
))
143 if (getsockname(fd
, &ep
.sa
, &len
))
146 if (ep
.sa
.sa_family
== AF_INET6
)
147 memcpy(local
, &ep
.in6
.sin6_addr
, sizeof(ep
.in6
.sin6_addr
));
149 memcpy(local
, &ep
.in
.sin_addr
, sizeof(ep
.in
.sin_addr
));
157 void *unet_read_file(const char *name
, size_t *len
)
163 f
= fopen(name
, "r");
167 if (fstat(fileno(f
), &st
) < 0)
170 if (*len
&& st
.st_size
> *len
)
173 data
= malloc(st
.st_size
);
177 if (fread(data
, 1, st
.st_size
, f
) != st
.st_size
) {
193 uint64_t unet_gettime(void)
197 clock_gettime(CLOCK_MONOTONIC
, &ts
);
202 static inline uint32_t
203 csum_tcpudp_nofold(uint32_t saddr
, uint32_t daddr
, uint32_t len
, uint8_t proto
)
209 #if __BYTE_ORDER == __LITTLE_ENDIAN
210 sum
+= (proto
+ len
) << 8;
215 sum
= (sum
& 0xffffffff) + (sum
>> 32);
216 sum
= (sum
& 0xffffffff) + (sum
>> 32);
218 return (uint32_t)sum
;
221 static inline uint32_t csum_add(uint32_t sum
, uint32_t addend
)
224 return sum
+ (sum
< addend
);
227 static inline uint16_t csum_fold(uint32_t sum
)
229 sum
= (sum
& 0xffff) + (sum
>> 16);
230 sum
= (sum
& 0xffff) + (sum
>> 16);
232 return (uint16_t)~sum
;
235 static uint32_t csum_partial(const void *buf
, int len
)
237 const uint16_t *data
= buf
;
246 #if __BYTE_ORDER == __LITTLE_ENDIAN
247 sum
+= *(uint8_t *)data
;
249 sum
+= *(uint8_t *)data
<< 8;
252 sum
= (sum
& 0xffff) + (sum
>> 16);
253 sum
= (sum
& 0xffff) + (sum
>> 16);
258 static void fixup_udpv4(void *hdr
, size_t hdrlen
, const void *data
, size_t len
)
261 struct udphdr
*udp
= hdr
+ ip
->ip_hl
* 4;
262 uint16_t udp_len
= sizeof(*udp
) + len
;
265 if ((void *)&udp
[1] > hdr
+ hdrlen
)
269 udp
->uh_ulen
= htons(udp_len
);
270 sum
= csum_tcpudp_nofold(*(uint32_t *)&ip
->ip_src
, *(uint32_t *)&ip
->ip_dst
,
272 sum
= csum_add(sum
, csum_partial(udp
, sizeof(*udp
)));
273 sum
= csum_add(sum
, csum_partial(data
, len
));
274 udp
->uh_sum
= csum_fold(sum
);
276 ip
->ip_len
= htons(hdrlen
+ len
);
278 ip
->ip_sum
= csum_fold(csum_partial(ip
, sizeof(*ip
)));
281 ip
->ip_len
= hdrlen
+ len
;
285 static void fixup_udpv6(void *hdr
, size_t hdrlen
, const void *data
, size_t len
)
287 struct ip6_hdr
*ip
= hdr
;
288 struct udphdr
*udp
= hdr
+ sizeof(*ip
);
289 uint16_t udp_len
= htons(sizeof(*udp
) + len
);
291 if ((void *)&udp
[1] > hdr
+ hdrlen
)
294 ip
->ip6_plen
= htons(sizeof(*udp
) + len
);
296 udp
->uh_ulen
= udp_len
;
297 udp
->uh_sum
= csum_fold(csum_partial(hdr
, sizeof(*ip
) + sizeof(*udp
)));
300 ip
->ip6_plen
= sizeof(*udp
) + len
;
304 static void fixup_ip_udp_header(void *hdr
, size_t hdrlen
, const void *data
, size_t len
)
306 if (hdrlen
>= sizeof(struct ip6_hdr
) + sizeof(struct udphdr
))
307 fixup_udpv6(hdr
, hdrlen
, data
, len
);
308 else if (hdrlen
>= sizeof(struct ip
) + sizeof(struct udphdr
))
309 fixup_udpv4(hdr
, hdrlen
, data
, len
);
312 int sendto_rawudp(int fd
, const void *addr
, void *ip_hdr
, size_t ip_hdrlen
,
313 const void *data
, size_t len
)
315 const struct sockaddr
*sa
= addr
;
316 struct iovec iov
[2] = {
317 { .iov_base
= ip_hdr
, .iov_len
= ip_hdrlen
},
318 { .iov_base
= (void *)data
, .iov_len
= len
}
320 struct msghdr msg
= {
321 .msg_name
= (void *)addr
,
323 .msg_iovlen
= ARRAY_SIZE(iov
),
326 if (sa
->sa_family
== AF_INET6
)
327 msg
.msg_namelen
= sizeof(struct sockaddr_in6
);
329 msg
.msg_namelen
= sizeof(struct sockaddr_in
);
331 fixup_ip_udp_header(ip_hdr
, ip_hdrlen
, data
, len
);
333 return sendmsg(fd
, &msg
, 0);