2 ctdb system specific code to manage raw sockets on linux
4 Copyright (C) Ronnie Sahlberg 2007
5 Copyright (C) Andrew Tridgell 2007
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, see <http://www.gnu.org/licenses/>.
22 #include "system/network.h"
23 #include "system/filesys.h"
24 #include "system/wait.h"
25 #include "../include/ctdb_private.h"
26 #include <netinet/if_ether.h>
27 #include <netinet/ip6.h>
28 #include <netinet/icmp6.h>
29 #include <net/if_arp.h>
30 #include <netpacket/packet.h>
31 #include <sys/prctl.h>
34 #define ETHERTYPE_IP6 0x86dd
38 calculate the tcp checksum for tcp over ipv6
40 static uint16_t tcp_checksum6(uint16_t *data
, size_t n
, struct ip6_hdr
*ip6
)
46 sum
+= uint16_checksum((uint16_t *)(void *)&ip6
->ip6_src
, 16);
47 sum
+= uint16_checksum((uint16_t *)(void *)&ip6
->ip6_dst
, 16);
50 phdr
[1] = htonl(ip6
->ip6_nxt
);
51 sum
+= uint16_checksum((uint16_t *)phdr
, 8);
53 sum
+= uint16_checksum(data
, n
);
55 sum
= (sum
& 0xFFFF) + (sum
>> 16);
56 sum
= (sum
& 0xFFFF) + (sum
>> 16);
66 send gratuitous arp reply after we have taken over an ip address
68 saddr is the address we are trying to claim
69 iface is the interface name we will be using to claim the address
71 int ctdb_sys_send_arp(const ctdb_sock_addr
*addr
, const char *iface
)
74 struct sockaddr_ll sall
;
75 struct ether_header
*eh
;
78 struct nd_neighbor_advert
*nd_na
;
79 struct nd_opt_hdr
*nd_oh
;
80 struct ifreq if_hwaddr
;
81 /* Size of IPv6 neighbor advertisement (with option) */
82 unsigned char buffer
[sizeof(struct ether_header
) +
83 sizeof(struct ip6_hdr
) +
84 sizeof(struct nd_neighbor_advert
) +
85 sizeof(struct nd_opt_hdr
) + ETH_ALEN
];
87 char bdcast
[] = {0xff,0xff,0xff,0xff,0xff,0xff};
92 ZERO_STRUCT(if_hwaddr
);
94 switch (addr
->ip
.sin_family
) {
96 s
= socket(PF_PACKET
, SOCK_RAW
, htons(ETHERTYPE_ARP
));
98 DEBUG(DEBUG_CRIT
,(__location__
" failed to open raw socket\n"));
102 DEBUG(DEBUG_DEBUG
, (__location__
" Created SOCKET FD:%d for sending arp\n", s
));
103 strncpy(ifr
.ifr_name
, iface
, sizeof(ifr
.ifr_name
)-1);
104 if (ioctl(s
, SIOCGIFINDEX
, &ifr
) < 0) {
105 DEBUG(DEBUG_CRIT
,(__location__
" interface '%s' not found\n", iface
));
110 /* get the mac address */
111 strncpy(if_hwaddr
.ifr_name
, iface
, sizeof(if_hwaddr
.ifr_name
)-1);
112 ret
= ioctl(s
, SIOCGIFHWADDR
, &if_hwaddr
);
115 DEBUG(DEBUG_CRIT
,(__location__
" ioctl failed\n"));
118 if (ARPHRD_LOOPBACK
== if_hwaddr
.ifr_hwaddr
.sa_family
) {
119 DEBUG(DEBUG_DEBUG
,("Ignoring loopback arp request\n"));
123 if (if_hwaddr
.ifr_hwaddr
.sa_family
!= AF_LOCAL
) {
126 DEBUG(DEBUG_CRIT
,(__location__
" not an ethernet address family (0x%x)\n",
127 if_hwaddr
.ifr_hwaddr
.sa_family
));
132 memset(buffer
, 0 , 64);
133 eh
= (struct ether_header
*)buffer
;
134 memset(eh
->ether_dhost
, 0xff, ETH_ALEN
);
135 memcpy(eh
->ether_shost
, if_hwaddr
.ifr_hwaddr
.sa_data
, ETH_ALEN
);
136 eh
->ether_type
= htons(ETHERTYPE_ARP
);
138 ah
= (struct arphdr
*)&buffer
[sizeof(struct ether_header
)];
139 ah
->ar_hrd
= htons(ARPHRD_ETHER
);
140 ah
->ar_pro
= htons(ETH_P_IP
);
141 ah
->ar_hln
= ETH_ALEN
;
144 /* send a gratious arp */
145 ah
->ar_op
= htons(ARPOP_REQUEST
);
146 ptr
= (char *)&ah
[1];
147 memcpy(ptr
, if_hwaddr
.ifr_hwaddr
.sa_data
, ETH_ALEN
);
149 memcpy(ptr
, &addr
->ip
.sin_addr
, 4);
151 memset(ptr
, 0, ETH_ALEN
);
153 memcpy(ptr
, &addr
->ip
.sin_addr
, 4);
156 sall
.sll_family
= AF_PACKET
;
158 memcpy(&sall
.sll_addr
[0], bdcast
, sall
.sll_halen
);
159 sall
.sll_protocol
= htons(ETH_P_ALL
);
160 sall
.sll_ifindex
= ifr
.ifr_ifindex
;
161 ret
= sendto(s
, buffer
, 64, 0, (struct sockaddr
*)&sall
, sizeof(sall
));
164 DEBUG(DEBUG_CRIT
,(__location__
" failed sendto\n"));
168 /* send unsolicited arp reply broadcast */
169 ah
->ar_op
= htons(ARPOP_REPLY
);
170 ptr
= (char *)&ah
[1];
171 memcpy(ptr
, if_hwaddr
.ifr_hwaddr
.sa_data
, ETH_ALEN
);
173 memcpy(ptr
, &addr
->ip
.sin_addr
, 4);
175 memcpy(ptr
, if_hwaddr
.ifr_hwaddr
.sa_data
, ETH_ALEN
);
177 memcpy(ptr
, &addr
->ip
.sin_addr
, 4);
180 ret
= sendto(s
, buffer
, 64, 0, (struct sockaddr
*)&sall
, sizeof(sall
));
182 DEBUG(DEBUG_CRIT
,(__location__
" failed sendto\n"));
190 s
= socket(PF_PACKET
, SOCK_RAW
, htons(ETHERTYPE_ARP
));
192 DEBUG(DEBUG_CRIT
,(__location__
" failed to open raw socket\n"));
196 DEBUG(DEBUG_DEBUG
, (__location__
" Created SOCKET FD:%d for sending arp\n", s
));
197 strncpy(ifr
.ifr_name
, iface
, sizeof(ifr
.ifr_name
));
198 if (ioctl(s
, SIOCGIFINDEX
, &ifr
) < 0) {
199 DEBUG(DEBUG_CRIT
,(__location__
" interface '%s' not found\n", iface
));
204 /* get the mac address */
205 strncpy(if_hwaddr
.ifr_name
, iface
, sizeof(if_hwaddr
.ifr_name
)-1);
206 ret
= ioctl(s
, SIOCGIFHWADDR
, &if_hwaddr
);
209 DEBUG(DEBUG_CRIT
,(__location__
" ioctl failed\n"));
212 if (ARPHRD_LOOPBACK
== if_hwaddr
.ifr_hwaddr
.sa_family
) {
213 DEBUG(DEBUG_DEBUG
,("Ignoring loopback arp request\n"));
217 if (if_hwaddr
.ifr_hwaddr
.sa_family
!= AF_LOCAL
) {
220 DEBUG(DEBUG_CRIT
,(__location__
" not an ethernet address family (0x%x)\n",
221 if_hwaddr
.ifr_hwaddr
.sa_family
));
225 memset(buffer
, 0 , sizeof(buffer
));
226 eh
= (struct ether_header
*)buffer
;
227 /* Ethernet multicast: 33:33:00:00:00:01 (see RFC2464,
228 * section 7) - note zeroes above! */
229 eh
->ether_dhost
[0] = eh
->ether_dhost
[1] = 0x33;
230 eh
->ether_dhost
[5] = 0x01;
231 memcpy(eh
->ether_shost
, if_hwaddr
.ifr_hwaddr
.sa_data
, ETH_ALEN
);
232 eh
->ether_type
= htons(ETHERTYPE_IP6
);
234 ip6
= (struct ip6_hdr
*)(eh
+1);
236 ip6
->ip6_plen
= htons(sizeof(*nd_na
) +
237 sizeof(struct nd_opt_hdr
) +
239 ip6
->ip6_nxt
= IPPROTO_ICMPV6
;
241 ip6
->ip6_src
= addr
->ip6
.sin6_addr
;
242 /* all-nodes multicast */
243 inet_pton(AF_INET6
, "ff02::1", &ip6
->ip6_dst
);
245 nd_na
= (struct nd_neighbor_advert
*)(ip6
+1);
246 nd_na
->nd_na_type
= ND_NEIGHBOR_ADVERT
;
247 nd_na
->nd_na_code
= 0;
248 nd_na
->nd_na_flags_reserved
= ND_NA_FLAG_OVERRIDE
;
249 nd_na
->nd_na_target
= addr
->ip6
.sin6_addr
;
250 /* Option: Target link-layer address */
251 nd_oh
= (struct nd_opt_hdr
*)(nd_na
+1);
252 nd_oh
->nd_opt_type
= ND_OPT_TARGET_LINKADDR
;
253 nd_oh
->nd_opt_len
= 1;
254 memcpy(&(nd_oh
+1)[0], if_hwaddr
.ifr_hwaddr
.sa_data
, ETH_ALEN
);
256 nd_na
->nd_na_cksum
= tcp_checksum6((uint16_t *)nd_na
,
257 ntohs(ip6
->ip6_plen
), ip6
);
259 sall
.sll_family
= AF_PACKET
;
261 memcpy(&sall
.sll_addr
[0], &eh
->ether_dhost
[0], sall
.sll_halen
);
262 sall
.sll_protocol
= htons(ETH_P_ALL
);
263 sall
.sll_ifindex
= ifr
.ifr_ifindex
;
264 ret
= sendto(s
, buffer
, sizeof(buffer
),
265 0, (struct sockaddr
*)&sall
, sizeof(sall
));
268 DEBUG(DEBUG_CRIT
,(__location__
" failed sendto\n"));
275 DEBUG(DEBUG_CRIT
,(__location__
" not an ipv4/ipv6 address (family is %u)\n", addr
->ip
.sin_family
));
284 simple TCP checksum - assumes data is multiple of 2 bytes long
286 static uint16_t tcp_checksum(uint16_t *data
, size_t n
, struct iphdr
*ip
)
288 uint32_t sum
= uint16_checksum(data
, n
);
290 sum
+= uint16_checksum((uint16_t *)(void *)&ip
->saddr
,
292 sum
+= uint16_checksum((uint16_t *)(void *)&ip
->daddr
,
294 sum
+= ip
->protocol
+ n
;
295 sum
= (sum
& 0xFFFF) + (sum
>> 16);
296 sum
= (sum
& 0xFFFF) + (sum
>> 16);
306 Send tcp segment from the specified IP/port to the specified
309 This is used to trigger the receiving host into sending its own ACK,
310 which should trigger early detection of TCP reset by the client
313 This can also be used to send RST segments (if rst is true) and also
314 if correct seq and ack numbers are provided.
316 int ctdb_sys_send_tcp(const ctdb_sock_addr
*dest
,
317 const ctdb_sock_addr
*src
,
318 uint32_t seq
, uint32_t ack
, int rst
)
324 ctdb_sock_addr
*tmpdest
;
334 switch (src
->ip
.sin_family
) {
337 ip4pkt
.ip
.version
= 4;
338 ip4pkt
.ip
.ihl
= sizeof(ip4pkt
.ip
)/4;
339 ip4pkt
.ip
.tot_len
= htons(sizeof(ip4pkt
));
341 ip4pkt
.ip
.protocol
= IPPROTO_TCP
;
342 ip4pkt
.ip
.saddr
= src
->ip
.sin_addr
.s_addr
;
343 ip4pkt
.ip
.daddr
= dest
->ip
.sin_addr
.s_addr
;
346 ip4pkt
.tcp
.source
= src
->ip
.sin_port
;
347 ip4pkt
.tcp
.dest
= dest
->ip
.sin_port
;
348 ip4pkt
.tcp
.seq
= seq
;
349 ip4pkt
.tcp
.ack_seq
= ack
;
354 ip4pkt
.tcp
.doff
= sizeof(ip4pkt
.tcp
)/4;
355 /* this makes it easier to spot in a sniffer */
356 ip4pkt
.tcp
.window
= htons(1234);
357 ip4pkt
.tcp
.check
= tcp_checksum((uint16_t *)&ip4pkt
.tcp
, sizeof(ip4pkt
.tcp
), &ip4pkt
.ip
);
359 /* open a raw socket to send this segment from */
360 s
= socket(AF_INET
, SOCK_RAW
, htons(IPPROTO_RAW
));
362 DEBUG(DEBUG_CRIT
,(__location__
" failed to open raw socket (%s)\n",
367 ret
= setsockopt(s
, SOL_IP
, IP_HDRINCL
, &one
, sizeof(one
));
369 DEBUG(DEBUG_CRIT
,(__location__
" failed to setup IP headers (%s)\n",
376 set_close_on_exec(s
);
378 ret
= sendto(s
, &ip4pkt
, sizeof(ip4pkt
), 0,
379 (const struct sockaddr
*)&dest
->ip
,
382 if (ret
!= sizeof(ip4pkt
)) {
383 DEBUG(DEBUG_CRIT
,(__location__
" failed sendto (%s)\n", strerror(errno
)));
389 ip6pkt
.ip6
.ip6_vfc
= 0x60;
390 ip6pkt
.ip6
.ip6_plen
= htons(20);
391 ip6pkt
.ip6
.ip6_nxt
= IPPROTO_TCP
;
392 ip6pkt
.ip6
.ip6_hlim
= 64;
393 ip6pkt
.ip6
.ip6_src
= src
->ip6
.sin6_addr
;
394 ip6pkt
.ip6
.ip6_dst
= dest
->ip6
.sin6_addr
;
396 ip6pkt
.tcp
.source
= src
->ip6
.sin6_port
;
397 ip6pkt
.tcp
.dest
= dest
->ip6
.sin6_port
;
398 ip6pkt
.tcp
.seq
= seq
;
399 ip6pkt
.tcp
.ack_seq
= ack
;
404 ip6pkt
.tcp
.doff
= sizeof(ip6pkt
.tcp
)/4;
405 /* this makes it easier to spot in a sniffer */
406 ip6pkt
.tcp
.window
= htons(1234);
407 ip6pkt
.tcp
.check
= tcp_checksum6((uint16_t *)&ip6pkt
.tcp
, sizeof(ip6pkt
.tcp
), &ip6pkt
.ip6
);
409 s
= socket(PF_INET6
, SOCK_RAW
, IPPROTO_RAW
);
411 DEBUG(DEBUG_CRIT
, (__location__
" Failed to open sending socket\n"));
415 /* sendto() dont like if the port is set and the socket is
418 tmpdest
= discard_const(dest
);
419 tmpport
= tmpdest
->ip6
.sin6_port
;
421 tmpdest
->ip6
.sin6_port
= 0;
422 ret
= sendto(s
, &ip6pkt
, sizeof(ip6pkt
), 0,
423 (const struct sockaddr
*)&dest
->ip6
,
425 tmpdest
->ip6
.sin6_port
= tmpport
;
428 if (ret
!= sizeof(ip6pkt
)) {
429 DEBUG(DEBUG_CRIT
,(__location__
" failed sendto (%s)\n", strerror(errno
)));
435 DEBUG(DEBUG_CRIT
,(__location__
" not an ipv4/v6 address\n"));
443 This function is used to open a raw socket to capture from
445 int ctdb_sys_open_capture_socket(const char *iface
, void **private_data
)
449 /* Open a socket to capture all traffic */
450 s
= socket(AF_PACKET
, SOCK_RAW
, htons(ETH_P_ALL
));
452 DEBUG(DEBUG_CRIT
,(__location__
" failed to open raw socket\n"));
456 DEBUG(DEBUG_DEBUG
, (__location__
" Created RAW SOCKET FD:%d for tcp tickle\n", s
));
459 set_close_on_exec(s
);
465 This function is used to do any additional cleanup required when closing
467 Note that the socket itself is closed automatically in the caller.
469 int ctdb_sys_close_capture_socket(void *private_data
)
476 called when the raw socket becomes readable
478 int ctdb_sys_read_tcp_packet(int s
, void *private_data
,
479 ctdb_sock_addr
*src
, ctdb_sock_addr
*dst
,
480 uint32_t *ack_seq
, uint32_t *seq
)
483 #define RCVPKTSIZE 100
484 char pkt
[RCVPKTSIZE
];
485 struct ether_header
*eth
;
490 ret
= recv(s
, pkt
, RCVPKTSIZE
, MSG_TRUNC
);
491 if (ret
< sizeof(*eth
)+sizeof(*ip
)) {
496 eth
= (struct ether_header
*)pkt
;
498 /* we want either IPv4 or IPv6 */
499 if (ntohs(eth
->ether_type
) == ETHERTYPE_IP
) {
501 ip
= (struct iphdr
*)(eth
+1);
503 /* We only want IPv4 packets */
504 if (ip
->version
!= 4) {
507 /* Dont look at fragments */
508 if ((ntohs(ip
->frag_off
)&0x1fff) != 0) {
511 /* we only want TCP */
512 if (ip
->protocol
!= IPPROTO_TCP
) {
516 /* make sure its not a short packet */
517 if (offsetof(struct tcphdr
, ack_seq
) + 4 +
518 (ip
->ihl
*4) + sizeof(*eth
) > ret
) {
522 tcp
= (struct tcphdr
*)((ip
->ihl
*4) + (char *)ip
);
524 /* tell the caller which one we've found */
525 src
->ip
.sin_family
= AF_INET
;
526 src
->ip
.sin_addr
.s_addr
= ip
->saddr
;
527 src
->ip
.sin_port
= tcp
->source
;
528 dst
->ip
.sin_family
= AF_INET
;
529 dst
->ip
.sin_addr
.s_addr
= ip
->daddr
;
530 dst
->ip
.sin_port
= tcp
->dest
;
531 *ack_seq
= tcp
->ack_seq
;
535 } else if (ntohs(eth
->ether_type
) == ETHERTYPE_IP6
) {
537 ip6
= (struct ip6_hdr
*)(eth
+1);
539 /* we only want TCP */
540 if (ip6
->ip6_nxt
!= IPPROTO_TCP
) {
545 tcp
= (struct tcphdr
*)(ip6
+1);
547 /* tell the caller which one we've found */
548 src
->ip6
.sin6_family
= AF_INET6
;
549 src
->ip6
.sin6_port
= tcp
->source
;
550 src
->ip6
.sin6_addr
= ip6
->ip6_src
;
552 dst
->ip6
.sin6_family
= AF_INET6
;
553 dst
->ip6
.sin6_port
= tcp
->dest
;
554 dst
->ip6
.sin6_addr
= ip6
->ip6_dst
;
556 *ack_seq
= tcp
->ack_seq
;
566 bool ctdb_sys_check_iface_exists(const char *iface
)
571 s
= socket(PF_PACKET
, SOCK_RAW
, 0);
573 /* We dont know if the interface exists, so assume yes */
574 DEBUG(DEBUG_CRIT
,(__location__
" failed to open raw socket\n"));
578 strncpy(ifr
.ifr_name
, iface
, sizeof(ifr
.ifr_name
)-1);
579 if (ioctl(s
, SIOCGIFINDEX
, &ifr
) < 0 && errno
== ENODEV
) {
580 DEBUG(DEBUG_CRIT
,(__location__
" interface '%s' not found\n", iface
));
589 int ctdb_get_peer_pid(const int fd
, pid_t
*peer_pid
)
592 socklen_t crl
= sizeof(struct ucred
);
594 if ((ret
= getsockopt(fd
, SOL_SOCKET
, SO_PEERCRED
, &cr
, &crl
) == 0)) {
601 * Find the process name from process ID
603 char *ctdb_get_process_name(pid_t pid
)
610 snprintf(path
, sizeof(path
), "/proc/%d/exe", pid
);
611 n
= readlink(path
, buf
, sizeof(buf
)-1);
616 /* Remove any extra fields */
618 ptr
= strtok(buf
, " ");
619 return (ptr
== NULL
? ptr
: strdup(ptr
));
625 int ctdb_set_process_name(const char *name
)
629 strncpy(procname
, name
, 15);
631 return prctl(PR_SET_NAME
, (unsigned long)procname
, 0, 0, 0);
635 * Parsing a line from /proc/locks,
637 static bool parse_proc_locks_line(char *line
, pid_t
*pid
,
638 struct ctdb_lock_info
*curlock
)
642 /* output of /proc/locks
645 * 1: POSIX ADVISORY WRITE 25945 fd:00:6424820 212 212
648 * 1: -> POSIX ADVISORY WRITE 25946 fd:00:6424820 212 212
652 ptr
= strtok_r(line
, " ", &saveptr
);
653 if (ptr
== NULL
) return false;
656 ptr
= strtok_r(NULL
, " ", &saveptr
);
657 if (ptr
== NULL
) return false;
658 if (strcmp(ptr
, "->") == 0) {
659 curlock
->waiting
= true;
660 ptr
= strtok_r(NULL
, " ", &saveptr
);
662 curlock
->waiting
= false;
666 if (ptr
== NULL
|| strcmp(ptr
, "POSIX") != 0) {
671 ptr
= strtok_r(NULL
, " ", &saveptr
);
672 if (ptr
== NULL
) return false;
675 ptr
= strtok_r(NULL
, " ", &saveptr
);
676 if (ptr
== NULL
) return false;
677 if (strcmp(ptr
, "READ") == 0) {
678 curlock
->read_only
= true;
679 } else if (strcmp(ptr
, "WRITE") == 0) {
680 curlock
->read_only
= false;
686 ptr
= strtok_r(NULL
, " ", &saveptr
);
687 if (ptr
== NULL
) return false;
690 /* MAJOR:MINOR:INODE */
691 ptr
= strtok_r(NULL
, " :", &saveptr
);
692 if (ptr
== NULL
) return false;
693 ptr
= strtok_r(NULL
, " :", &saveptr
);
694 if (ptr
== NULL
) return false;
695 ptr
= strtok_r(NULL
, " :", &saveptr
);
696 if (ptr
== NULL
) return false;
697 curlock
->inode
= atol(ptr
);
700 ptr
= strtok_r(NULL
, " ", &saveptr
);
701 if (ptr
== NULL
) return false;
702 curlock
->start
= atol(ptr
);
705 ptr
= strtok_r(NULL
, " ", &saveptr
);
706 if (ptr
== NULL
) return false;
707 if (strncmp(ptr
, "EOF", 3) == 0) {
708 curlock
->end
= (off_t
)-1;
710 curlock
->end
= atol(ptr
);
717 * Find information of lock being waited on for given process ID
719 bool ctdb_get_lock_info(pid_t req_pid
, struct ctdb_lock_info
*lock_info
)
722 struct ctdb_lock_info curlock
;
727 if ((fp
= fopen("/proc/locks", "r")) == NULL
) {
728 DEBUG(DEBUG_ERR
, ("Failed to read locks information"));
731 while (fgets(buf
, sizeof(buf
), fp
) != NULL
) {
732 if (! parse_proc_locks_line(buf
, &pid
, &curlock
)) {
735 if (pid
== req_pid
&& curlock
.waiting
) {
736 *lock_info
= curlock
;
747 * Find process ID which holds an overlapping byte lock for required
748 * inode and byte range.
750 bool ctdb_get_blocker_pid(struct ctdb_lock_info
*reqlock
, pid_t
*blocker_pid
)
753 struct ctdb_lock_info curlock
;
758 if ((fp
= fopen("/proc/locks", "r")) == NULL
) {
759 DEBUG(DEBUG_ERR
, ("Failed to read locks information"));
762 while (fgets(buf
, sizeof(buf
), fp
) != NULL
) {
763 if (! parse_proc_locks_line(buf
, &pid
, &curlock
)) {
767 if (curlock
.waiting
) {
771 if (curlock
.inode
!= reqlock
->inode
) {
775 if (curlock
.start
> reqlock
->end
||
776 curlock
.end
< reqlock
->start
) {
777 /* Outside the required range */