401c39620fd90604b051293235278ca835017a5e
[samba.git] / ctdb / common / system_freebsd.c
1 /* 
2    ctdb system specific code to manage raw sockets on freebsd
3
4    Copyright (C) Ronnie Sahlberg  2007
5    Copyright (C) Andrew Tridgell  2007
6    Copyright (C) Marc Dequènes (Duck) 2009
7    Copyright (C) Volker Lendecke 2012
8
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 3 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, see <http://www.gnu.org/licenses/>.
21
22
23   This file is a copy of 'common/system_linux.c' adapted for Hurd^W kFreeBSD
24   needs, and inspired by 'common/system_aix.c' for the pcap usage.
25 */
26
27 #include "replace.h"
28 #include "system/network.h"
29 #include "system/filesys.h"
30 #include "system/wait.h"
31
32 #include "lib/util/debug.h"
33
34 #include "protocol/protocol.h"
35
36 #include <net/ethernet.h>
37 #include <netinet/ip6.h>
38 #include <net/if_arp.h>
39 #include <pcap.h>
40
41 #include "common/logging.h"
42 #include "common/system.h"
43
44 #ifndef ETHERTYPE_IP6
45 #define ETHERTYPE_IP6 0x86dd
46 #endif
47
48 /*
49   calculate the tcp checksum for tcp over ipv6
50 */
51 static uint16_t tcp_checksum6(uint16_t *data, size_t n, struct ip6_hdr *ip6)
52 {
53         uint32_t phdr[2];
54         uint32_t sum = 0;
55         uint16_t sum2;
56
57         sum += uint16_checksum((uint16_t *)(void *)&ip6->ip6_src, 16);
58         sum += uint16_checksum((uint16_t *)(void *)&ip6->ip6_dst, 16);
59
60         phdr[0] = htonl(n);
61         phdr[1] = htonl(ip6->ip6_nxt);
62         sum += uint16_checksum((uint16_t *)phdr, 8);
63
64         sum += uint16_checksum(data, n);
65
66         sum = (sum & 0xFFFF) + (sum >> 16);
67         sum = (sum & 0xFFFF) + (sum >> 16);
68         sum2 = htons(sum);
69         sum2 = ~sum2;
70         if (sum2 == 0) {
71                 return 0xFFFF;
72         }
73         return sum2;
74 }
75
76 /*
77   send gratuitous arp reply after we have taken over an ip address
78
79   saddr is the address we are trying to claim
80   iface is the interface name we will be using to claim the address
81  */
82 int ctdb_sys_send_arp(const ctdb_sock_addr *addr, const char *iface)
83 {
84         /* FIXME FreeBSD: We don't do gratuitous arp yet */
85         return -1;
86 }
87
88
89 /*
90   simple TCP checksum - assumes data is multiple of 2 bytes long
91  */
92 static uint16_t tcp_checksum(uint16_t *data, size_t n, struct ip *ip)
93 {
94         uint32_t sum = uint16_checksum(data, n);
95         uint16_t sum2;
96         sum += uint16_checksum((uint16_t *)(void *)&ip->ip_src,
97                                sizeof(ip->ip_src));
98         sum += uint16_checksum((uint16_t *)(void *)&ip->ip_dst,
99                                sizeof(ip->ip_dst));
100         sum += ip->ip_p + n;
101         sum = (sum & 0xFFFF) + (sum >> 16);
102         sum = (sum & 0xFFFF) + (sum >> 16);
103         sum2 = htons(sum);
104         sum2 = ~sum2;
105         if (sum2 == 0) {
106                 return 0xFFFF;
107         }
108         return sum2;
109 }
110
111 /*
112   Send tcp segment from the specified IP/port to the specified
113   destination IP/port. 
114
115   This is used to trigger the receiving host into sending its own ACK,
116   which should trigger early detection of TCP reset by the client
117   after IP takeover
118
119   This can also be used to send RST segments (if rst is true) and also
120   if correct seq and ack numbers are provided.
121  */
122 int ctdb_sys_send_tcp(const ctdb_sock_addr *dest, 
123                       const ctdb_sock_addr *src,
124                       uint32_t seq, uint32_t ack, int rst)
125 {
126         int s;
127         int ret;
128         uint32_t one = 1;
129         uint16_t tmpport;
130         ctdb_sock_addr *tmpdest;
131         struct {
132                 struct ip ip;
133                 struct tcphdr tcp;
134         } ip4pkt;
135         struct {
136                 struct ip6_hdr ip6;
137                 struct tcphdr tcp;
138         } ip6pkt;
139
140         switch (src->ip.sin_family) {
141         case AF_INET:
142                 ZERO_STRUCT(ip4pkt);
143                 ip4pkt.ip.ip_v  = 4;
144                 ip4pkt.ip.ip_hl    = sizeof(ip4pkt.ip)/4;
145                 ip4pkt.ip.ip_len   = htons(sizeof(ip4pkt));
146                 ip4pkt.ip.ip_ttl   = 255;
147                 ip4pkt.ip.ip_p     = IPPROTO_TCP;
148                 ip4pkt.ip.ip_src.s_addr = src->ip.sin_addr.s_addr;
149                 ip4pkt.ip.ip_dst.s_addr = dest->ip.sin_addr.s_addr;
150                 ip4pkt.ip.ip_sum   = 0;
151
152                 ip4pkt.tcp.th_sport = src->ip.sin_port;
153                 ip4pkt.tcp.th_dport = dest->ip.sin_port;
154                 ip4pkt.tcp.th_seq   = seq;
155                 ip4pkt.tcp.th_ack   = ack;
156                 ip4pkt.tcp.th_flags = 0;
157                 ip4pkt.tcp.th_flags |= TH_ACK;
158                 if (rst) {
159                         ip4pkt.tcp.th_flags |= TH_RST;
160                 }
161                 ip4pkt.tcp.th_off   = sizeof(ip4pkt.tcp)/4;
162                 /* this makes it easier to spot in a sniffer */
163                 ip4pkt.tcp.th_win   = htons(1234);
164                 ip4pkt.tcp.th_sum   = tcp_checksum((uint16_t *)&ip4pkt.tcp, sizeof(ip4pkt.tcp), &ip4pkt.ip);
165
166                 /* open a raw socket to send this segment from */
167                 s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
168                 if (s == -1) {
169                         DEBUG(DEBUG_CRIT,(__location__ " failed to open raw socket (%s)\n",
170                                  strerror(errno)));
171                         return -1;
172                 }
173
174                 ret = setsockopt(s, IPPROTO_IP, IP_HDRINCL, &one, sizeof(one));
175                 if (ret != 0) {
176                         DEBUG(DEBUG_CRIT,(__location__ " failed to setup IP headers (%s)\n",
177                                  strerror(errno)));
178                         close(s);
179                         return -1;
180                 }
181
182                 set_nonblocking(s);
183                 set_close_on_exec(s);
184
185                 ret = sendto(s, &ip4pkt, sizeof(ip4pkt), 0,
186                              (const struct sockaddr *)&dest->ip,
187                              sizeof(dest->ip));
188                 close(s);
189                 if (ret != sizeof(ip4pkt)) {
190                         DEBUG(DEBUG_CRIT,(__location__ " failed sendto (%s)\n", strerror(errno)));
191                         return -1;
192                 }
193                 break;
194         case AF_INET6:
195                 ZERO_STRUCT(ip6pkt);
196                 ip6pkt.ip6.ip6_vfc  = 0x60;
197                 ip6pkt.ip6.ip6_plen = htons(20);
198                 ip6pkt.ip6.ip6_nxt  = IPPROTO_TCP;
199                 ip6pkt.ip6.ip6_hlim = 64;
200                 ip6pkt.ip6.ip6_src  = src->ip6.sin6_addr;
201                 ip6pkt.ip6.ip6_dst  = dest->ip6.sin6_addr;
202
203                 ip6pkt.tcp.th_sport = src->ip6.sin6_port;
204                 ip6pkt.tcp.th_dport = dest->ip6.sin6_port;
205                 ip6pkt.tcp.th_seq   = seq;
206                 ip6pkt.tcp.th_ack   = ack;
207                 ip6pkt.tcp.th_flags = 0;
208                 ip6pkt.tcp.th_flags |= TH_ACK;
209                 if (rst) {
210                         ip6pkt.tcp.th_flags |= TH_RST;
211                 }
212                 ip6pkt.tcp.th_off   = sizeof(ip6pkt.tcp)/4;
213                 /* this makes it easier to spot in a sniffer */
214                 ip6pkt.tcp.th_win   = htons(1234);
215                 ip6pkt.tcp.th_sum   = tcp_checksum6((uint16_t *)&ip6pkt.tcp, sizeof(ip6pkt.tcp), &ip6pkt.ip6);
216
217                 s = socket(PF_INET6, SOCK_RAW, IPPROTO_RAW);
218                 if (s == -1) {
219                         DEBUG(DEBUG_CRIT, (__location__ " Failed to open sending socket\n"));
220                         return -1;
221
222                 }
223                 /* sendto() don't like if the port is set and the socket is
224                    in raw mode.
225                 */
226                 tmpdest = discard_const(dest);
227                 tmpport = tmpdest->ip6.sin6_port;
228
229                 tmpdest->ip6.sin6_port = 0;
230                 ret = sendto(s, &ip6pkt, sizeof(ip6pkt), 0,
231                              (const struct sockaddr *)&dest->ip6,
232                              sizeof(dest->ip6));
233                 tmpdest->ip6.sin6_port = tmpport;
234                 close(s);
235
236                 if (ret != sizeof(ip6pkt)) {
237                         DEBUG(DEBUG_CRIT,(__location__ " failed sendto (%s)\n", strerror(errno)));
238                         return -1;
239                 }
240                 break;
241
242         default:
243                 DEBUG(DEBUG_CRIT,(__location__ " not an ipv4/v6 address\n"));
244                 return -1;
245         }
246
247         return 0;
248 }
249
250 /* 
251    This function is used to open a raw socket to capture from
252  */
253 int ctdb_sys_open_capture_socket(const char *iface, void **private_data)
254 {
255         pcap_t *pt;
256
257         pt=pcap_open_live(iface, 100, 0, 0, NULL);
258         if (pt == NULL) {
259                 DEBUG(DEBUG_CRIT,("Failed to open capture device %s\n", iface));
260                 return -1;
261         }
262         *((pcap_t **)private_data) = pt;
263
264         return pcap_fileno(pt);
265 }
266
267 /* This function is used to close the capture socket
268  */
269 int ctdb_sys_close_capture_socket(void *private_data)
270 {
271         pcap_t *pt = (pcap_t *)private_data;
272         pcap_close(pt);
273         return 0;
274 }
275
276
277 /*
278   called when the raw socket becomes readable
279  */
280 int ctdb_sys_read_tcp_packet(int s, void *private_data, 
281                         ctdb_sock_addr *src, ctdb_sock_addr *dst,
282                         uint32_t *ack_seq, uint32_t *seq)
283 {
284         int ret;
285 #define RCVPKTSIZE 100
286         char pkt[RCVPKTSIZE];
287         struct ether_header *eth;
288         struct ip *ip;
289         struct ip6_hdr *ip6;
290         struct tcphdr *tcp;
291
292         ret = recv(s, pkt, RCVPKTSIZE, MSG_TRUNC);
293         if (ret < sizeof(*eth)+sizeof(*ip)) {
294                 return -1;
295         }
296
297         /* Ethernet */
298         eth = (struct ether_header *)pkt;
299
300         /* we want either IPv4 or IPv6 */
301         if (ntohs(eth->ether_type) == ETHERTYPE_IP) {
302                 /* IP */
303                 ip = (struct ip *)(eth+1);
304
305                 /* We only want IPv4 packets */
306                 if (ip->ip_v != 4) {
307                         return -1;
308                 }
309                 /* Dont look at fragments */
310                 if ((ntohs(ip->ip_off)&0x1fff) != 0) {
311                         return -1;
312                 }
313                 /* we only want TCP */
314                 if (ip->ip_p != IPPROTO_TCP) {
315                         return -1;
316                 }
317
318                 /* make sure its not a short packet */
319                 if (offsetof(struct tcphdr, th_ack) + 4 + 
320                     (ip->ip_hl*4) + sizeof(*eth) > ret) {
321                         return -1;
322                 }
323                 /* TCP */
324                 tcp = (struct tcphdr *)((ip->ip_hl*4) + (char *)ip);
325
326                 /* tell the caller which one we've found */
327                 src->ip.sin_family      = AF_INET;
328                 src->ip.sin_addr.s_addr = ip->ip_src.s_addr;
329                 src->ip.sin_port        = tcp->th_sport;
330                 dst->ip.sin_family      = AF_INET;
331                 dst->ip.sin_addr.s_addr = ip->ip_dst.s_addr;
332                 dst->ip.sin_port        = tcp->th_dport;
333                 *ack_seq                = tcp->th_ack;
334                 *seq                    = tcp->th_seq;
335
336                 return 0;
337         } else if (ntohs(eth->ether_type) == ETHERTYPE_IP6) {
338                 /* IP6 */
339                 ip6 = (struct ip6_hdr *)(eth+1);
340
341                 /* we only want TCP */
342                 if (ip6->ip6_nxt != IPPROTO_TCP) {
343                         return -1;
344                 }
345
346                 /* TCP */
347                 tcp = (struct tcphdr *)(ip6+1);
348
349                 /* tell the caller which one we've found */
350                 src->ip6.sin6_family = AF_INET6;
351                 src->ip6.sin6_port   = tcp->th_sport;
352                 src->ip6.sin6_addr   = ip6->ip6_src;
353
354                 dst->ip6.sin6_family = AF_INET6;
355                 dst->ip6.sin6_port   = tcp->th_dport;
356                 dst->ip6.sin6_addr   = ip6->ip6_dst;
357
358                 *ack_seq             = tcp->th_ack;
359                 *seq                 = tcp->th_seq;
360
361                 return 0;
362         }
363
364         return -1;
365 }
366
367 bool ctdb_sys_check_iface_exists(const char *iface)
368 {
369         /* FIXME FreeBSD: Interface always considered present */
370         return true;
371 }
372
373 int ctdb_get_peer_pid(const int fd, pid_t *peer_pid)
374 {
375         /* FIXME FreeBSD: get_peer_pid not implemented */
376         return 1;
377 }