/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 | } |