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