Coverage Report

Created: 2026-09-28 06:27

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/freeradius-server/src/lib/util/net.c
Line
Count
Source
1
/*
2
 *   This program is free software; you can redistribute it and/or modify
3
 *   it under the terms of the GNU General Public License, version 2 of the
4
 *   License as published by the Free Software Foundation.
5
 *
6
 *   This program is distributed in the hope that it will be useful,
7
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
8
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
9
 *   GNU General Public License for more details.
10
 *
11
 *   You should have received a copy of the GNU General Public License
12
 *   along with this program; if not, write to the Free Software
13
 *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
14
 */
15
16
/** Functions for parsing raw network packets
17
 *
18
 * @file src/lib/util/net.c
19
 *
20
 * @author Arran Cudbard-Bell (a.cudbardb@freeradius.org)
21
 * @copyright 2014-2015 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
22
 */
23
#include <freeradius-devel/util/net.h>
24
25
/** Strings for L4 protocols
26
 *
27
 */
28
fr_table_num_sorted_t const fr_net_ip_proto_table[] = {
29
  { L("ICMP"),  IPPROTO_ICMP  },
30
  { L("ICMPv6"),  IPPROTO_ICMPV6  },
31
  { L("TCP"), IPPROTO_TCP },
32
  { L("UDP"), IPPROTO_UDP }
33
};
34
size_t fr_net_ip_proto_table_len = NUM_ELEMENTS(fr_net_ip_proto_table);
35
36
/** Strings for socket types
37
 *
38
 */
39
fr_table_num_sorted_t const fr_net_sock_type_table[] = {
40
  { L("TCP"), SOCK_STREAM },
41
  { L("UDP"), SOCK_DGRAM  }
42
};
43
size_t fr_net_sock_type_table_len = NUM_ELEMENTS(fr_net_sock_type_table);
44
45
/** Strings for address families
46
 *
47
 */
48
fr_table_num_sorted_t const fr_net_af_table[] = {
49
  { L("IPv4"),  AF_INET   },
50
  { L("IPv6"),  AF_INET6  }
51
};
52
size_t fr_net_af_table_len = NUM_ELEMENTS(fr_net_af_table);
53
54
/** Check UDP header is valid
55
 *
56
 * @param data Pointer to the start of the UDP header
57
 * @param remaining bits of received packet
58
 * @param ip pointer to IP header structure
59
 * @return
60
 *  - 1 if checksum is incorrect.
61
 *  - 0 if UDP payload length and checksum are correct
62
 *  - -1 on validation error.
63
 */
64
 int fr_udp_header_check(uint8_t const *data, uint16_t remaining, ip_header_t const * ip)
65
0
 {
66
0
  int ret = 0;
67
0
  udp_header_t const  *udp;
68
69
  /*
70
   *  UDP header validation.
71
   */
72
0
  uint16_t udp_len;
73
0
  ssize_t actual_len;
74
0
  uint16_t expected;
75
76
0
  udp = (udp_header_t const *)data;
77
0
  udp_len = ntohs(udp->len);
78
0
  actual_len = remaining;
79
  /* Truncated data */
80
0
  if (udp_len > actual_len) {
81
0
    fr_strerror_printf("packet too small by %zi bytes, UDP header + Payload should be %hu bytes",
82
0
           (udp_len - actual_len), udp_len);
83
0
    return -1;
84
0
  }
85
  /* Trailing data */
86
0
  else if (udp_len < actual_len) {
87
0
    fr_strerror_printf("Packet too big by %zi bytes, UDP header + Payload should be %hu bytes",
88
0
           (actual_len - udp_len), udp_len);
89
0
    return -1;
90
0
  }
91
92
  /* coverity[tainted_data] */
93
0
  expected = fr_udp_checksum((uint8_t const *) udp, udp_len, udp->checksum,
94
0
           ip->ip_src, ip->ip_dst);
95
0
  if (udp->checksum != expected) {
96
0
    fr_strerror_printf("UDP checksum invalid, packet: 0x%04hx calculated: 0x%04hx",
97
0
           ntohs(udp->checksum), ntohs(expected));
98
    /* Not a fatal error */
99
0
    ret = 1;
100
0
  }
101
102
0
  return ret;
103
0
 }
104
105
/** Calculate UDP checksum
106
 *
107
 * Zero out UDP checksum in UDP header before calling #fr_udp_checksum to get 'expected' checksum.
108
 *
109
 * @param data Pointer to the start of the UDP header
110
 * @param len value of udp length field in host byte order. Must be validated to make
111
 *    sure it won't overrun data buffer.
112
 * @param checksum current checksum, leave as 0 to just enable validation.
113
 * @param src_addr in network byte order.
114
 * @param dst_addr in network byte order.
115
 * @return
116
 *  - 0 if the checksum is correct.
117
 *  - !0 if checksum is incorrect.
118
 */
119
uint16_t fr_udp_checksum(uint8_t const *data, uint16_t len, uint16_t checksum,
120
       struct in_addr const src_addr, struct in_addr const dst_addr)
121
0
{
122
0
  uint64_t sum = 0; /* using 64bits avoids overflow check */
123
0
  uint16_t const *p = (uint16_t const *)data;
124
125
0
  uint16_t const *ip_src = (void const *) &src_addr.s_addr;
126
0
  uint16_t const *ip_dst = (void const *) &dst_addr.s_addr;
127
0
  uint16_t i;
128
129
0
  sum += *(ip_src++);
130
0
  sum += *ip_src;
131
0
  sum += *(ip_dst++);
132
0
  sum += *ip_dst;
133
134
0
  sum += htons(IPPROTO_UDP);
135
0
  sum += htons(len);
136
137
0
  for (i = len; i > 1; i -= 2) sum += *p++;
138
0
  if (i) sum += (0xff & *(uint8_t const *)p);
139
140
0
  sum -= checksum;
141
142
0
  while (sum >> 16) sum = (sum & 0xffff) + (sum >> 16);
143
144
0
  return ((uint16_t) ~sum);
145
0
}
146
147
/** Calculate IP header checksum.
148
 *
149
 * Zero out IP header checksum in IP header before calling fr_ip_header_checksum to get 'expected' checksum.
150
 *
151
 * @param data Pointer to the start of the IP header
152
 * @param ihl value of ip header length field (number of 32 bit words)
153
 */
154
uint16_t fr_ip_header_checksum(uint8_t const *data, uint8_t ihl)
155
0
{
156
0
  uint64_t sum = 0;
157
0
  uint16_t const *p = (uint16_t const *)data;
158
159
0
  uint8_t nwords = (ihl << 1); /* number of 16-bit words */
160
161
0
  for (sum = 0; nwords > 0; nwords--) {
162
0
    sum += *p++;
163
0
  }
164
0
  sum = (sum >> 16) + (sum & 0xffff);
165
0
  sum += (sum >> 16);
166
0
  return ((uint16_t) ~sum);
167
0
}
168
169
uint16_t fr_ip6_pseudo_header_checksum(struct in6_addr const *src, struct in6_addr const *dst, uint16_t ip_len, uint8_t ip_next)
170
0
{
171
0
  uint64_t sum = 0;
172
0
  ip_pseudo_header6_t ip6; /* Keep correct alignment for the pointer */
173
0
  uint8_t const *p = (uint8_t const *) &ip6;
174
0
  int8_t nwords = sizeof(ip6) >> 1; /* number of 16-bit words */
175
176
0
  memcpy(&ip6.ip_src, src, sizeof(ip6.ip_src));
177
0
  memcpy(&ip6.ip_dst, dst, sizeof(ip6.ip_dst));
178
0
  ip6.ip_len = ip_len;
179
0
  ip6.ip_next = ip_next;
180
181
0
  for (sum = 0; nwords > 0; nwords--) {
182
0
    uint16_t word;
183
0
    memcpy(&word, p, sizeof(word)); /* Can't use a uint16_t * as GCC flags this for unaligned access */
184
0
    sum += word;
185
0
    p += sizeof(word);
186
0
  }
187
0
  sum = (sum >> 16) + (sum & 0xffff);
188
0
  sum += (sum >> 16);
189
0
  return ((uint16_t) ~sum);
190
0
}