xdp-util.h revision 1.1 1 1.1 christos /*
2 1.1 christos * xdp-util.h -- set of xdp related helpers
3 1.1 christos *
4 1.1 christos * Copyright (c) 2024, NLnet Labs. All rights reserved.
5 1.1 christos *
6 1.1 christos * See LICENSE for the license.
7 1.1 christos *
8 1.1 christos */
9 1.1 christos
10 1.1 christos #ifndef XDP_UTIL_H
11 1.1 christos #define XDP_UTIL_H
12 1.1 christos
13 1.1 christos #include <linux/ip.h>
14 1.1 christos #include <linux/ipv6.h>
15 1.1 christos #include <linux/types.h>
16 1.1 christos #include <linux/udp.h>
17 1.1 christos
18 1.1 christos /*
19 1.1 christos * Get number of combined or rx queues available on ifname.
20 1.1 christos */
21 1.1 christos int ethtool_channels_get(char const *ifname);
22 1.1 christos
23 1.1 christos /*
24 1.1 christos * Set capabilities to only include the ones needed for using AF_XDP/
25 1.1 christos */
26 1.1 christos void set_caps(int unset_setid_caps);
27 1.1 christos
28 1.1 christos /*
29 1.1 christos * Add u16 to checksum value and preserve one's complement sum.
30 1.1 christos */
31 1.1 christos static inline __sum16 csum16_add(__sum16 csum, __be16 addend) {
32 1.1 christos uint16_t res = (uint16_t)csum;
33 1.1 christos
34 1.1 christos res += (__u16)addend;
35 1.1 christos return (__sum16)(res + (res < (__u16)addend));
36 1.1 christos }
37 1.1 christos
38 1.1 christos /*
39 1.1 christos * Subtract u16 from checksum value and preserve one's complement sum.
40 1.1 christos */
41 1.1 christos static inline __sum16 csum16_sub(__sum16 csum, __be16 addend) {
42 1.1 christos return csum16_add(csum, ~addend);
43 1.1 christos }
44 1.1 christos
45 1.1 christos /*
46 1.1 christos * Replace u16 from checksum value and preserve one's complement sum.
47 1.1 christos */
48 1.1 christos static inline void csum16_replace(__sum16 *sum, __be16 old, __be16 new) {
49 1.1 christos *sum = ~csum16_add(csum16_sub(~(*sum), old), new);
50 1.1 christos }
51 1.1 christos
52 1.1 christos /*
53 1.1 christos * Sum up _data_len amount of 16-bit words in _data and add to result.
54 1.1 christos */
55 1.1 christos static inline void csum_add_data(uint32_t *result,
56 1.1 christos const void *_data,
57 1.1 christos uint32_t len) {
58 1.1 christos const uint16_t *data = _data;
59 1.1 christos while (len > 1) {
60 1.1 christos *result += *data++;
61 1.1 christos len -= 2;
62 1.1 christos }
63 1.1 christos if (len)
64 1.1 christos *result += *data & 0xff;
65 1.1 christos // *result += *(uint8_t *)data;
66 1.1 christos }
67 1.1 christos
68 1.1 christos /*
69 1.1 christos * Add single 16-bit words to result.
70 1.1 christos */
71 1.1 christos static inline void csum_add_u16(uint32_t *result, uint16_t x) { *result += x; }
72 1.1 christos
73 1.1 christos /*
74 1.1 christos * Apply one's complement to result.
75 1.1 christos */
76 1.1 christos static inline void csum_reduce(uint32_t *result) {
77 1.1 christos while (*result >> 16)
78 1.1 christos *result = (*result & 0xffff) + (*result >> 16);
79 1.1 christos }
80 1.1 christos
81 1.1 christos /*
82 1.1 christos * Calculate UDP checksum with IPv6 pseudo-header
83 1.1 christos */
84 1.1 christos static inline uint16_t calc_csum_udp6(struct udphdr *udp, struct ipv6hdr *ipv6) {
85 1.1 christos uint32_t sum = 0;
86 1.1 christos sum += udp->len;
87 1.1 christos sum += htons(IPPROTO_UDP);
88 1.1 christos csum_add_data(&sum, &ipv6->saddr, sizeof(ipv6->saddr));
89 1.1 christos csum_add_data(&sum, &ipv6->daddr, sizeof(ipv6->daddr));
90 1.1 christos
91 1.1 christos udp->check = 0;
92 1.1 christos csum_add_data(&sum, udp, ntohs(udp->len));
93 1.1 christos /* maybe restore previous checksum to remove side effects? */
94 1.1 christos
95 1.1 christos // reduces sum to 16bit
96 1.1 christos csum_reduce(&sum);
97 1.1 christos
98 1.1 christos if (sum != 0xffff)
99 1.1 christos return (uint16_t) ~sum;
100 1.1 christos else
101 1.1 christos return (uint16_t) sum;
102 1.1 christos }
103 1.1 christos
104 1.1 christos /*
105 1.1 christos * Calculate UDP checksum with IPv4 pseudo-header
106 1.1 christos */
107 1.1 christos static inline uint16_t calc_csum_udp4(struct udphdr *udp, struct iphdr *ipv4) {
108 1.1 christos uint32_t sum = 0;
109 1.1 christos sum += udp->len;
110 1.1 christos sum += htons(IPPROTO_UDP);
111 1.1 christos csum_add_data(&sum, &ipv4->saddr, sizeof(ipv4->saddr));
112 1.1 christos csum_add_data(&sum, &ipv4->daddr, sizeof(ipv4->daddr));
113 1.1 christos
114 1.1 christos udp->check = 0;
115 1.1 christos csum_add_data(&sum, udp, ntohs(udp->len));
116 1.1 christos /* maybe restore previous checksum to remove side effects? */
117 1.1 christos
118 1.1 christos // reduces sum to 16bit
119 1.1 christos csum_reduce(&sum);
120 1.1 christos
121 1.1 christos if (sum != 0xffff)
122 1.1 christos return (uint16_t) ~sum;
123 1.1 christos else
124 1.1 christos return (uint16_t) sum;
125 1.1 christos }
126 1.1 christos
127 1.1 christos #endif /* XDP_UTIL_H */
128