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