1/* -*- c-basic-offset: 8; -*-
2 *
3 * Copyright (c) 1993 W. Richard Stevens.  All rights reserved.
4 * Permission to use or modify this software and its documentation only for
5 * educational purposes and without fee is hereby granted, provided that
6 * the above copyright notice appear in all copies.  The author makes no
7 * representations about the suitability of this software for any purpose.
8 * It is provided "as is" without express or implied warranty.
9 */
10
11#include <stdio.h>
12#include "sock.h"
13#include <netinet/ip.h>
14#include <netdb.h>
15#include <netinet/in.h>
16#include <arpa/inet.h>
17
18/*
19 * There is a fundamental limit of 9 IP addresses in a source route.
20 * But we allocate sroute_opt[44] with room for 10 IP addresses (and
21 * the 3-byte source route type/len/offset) because with the BSD
22 * IP_OPTIONS socket option we can specify up to 9 addresses, followed
23 * by the destination address.  The in_pcbopts() function in the kernel
24 * then takes the final address in the list (the destination) and moves
25 * it to the front, as shown in Figure 9.33 of "TCP/IP Illustrated,
26 * Volume 2".  Also note that this destination address that we pass as
27 * the final IP address in our array overrides the destination address
28 * of the sendto() (Figure 9.28 of Volume 2).
29 */
30
31u_char	sroute_opt[44];		/* some implementations require this to be
32							   on a 4-byte boundary */
33u_char	*optr;				/* pointer into options being formed */
34
35/*
36 * Process either the -g (loose) or -G (strict) command-line option,
37 * specifying one hop in a source route.
38 * Either option can be specified up to 9 times.
39 * With IPv4 the entire source route is either loose or strict, but we
40 * set the source route type field based on the first option encountered,
41 * either -g or -G.
42 */
43
44void
45sroute_doopt(int strict, char *argptr)
46{
47	struct in_addr	inaddr;
48	struct hostent	*hp;
49
50	if (sroute_cnt >= 9)
51		err_quit("too many source routes with: %s", argptr);
52
53	if (sroute_cnt == 0) {	/* first one */
54		bzero(sroute_opt, sizeof(sroute_opt));
55		optr = sroute_opt;
56		*optr++ = strict ? IPOPT_SSRR : IPOPT_LSRR;
57		optr++;			/* we fill in the total length later */
58		*optr++ = 4;	/* ptr to first source-route address */
59	}
60
61	if (inet_aton(argptr, &inaddr) == 1) {
62		bcopy(&inaddr, optr, sizeof(u_long));	/* dotted decimal */
63		if (verbose)
64			fprintf(stderr, "source route to %s\n", inet_ntoa(inaddr));
65	} else if ( (hp = gethostbyname(argptr)) != NULL) {
66		bcopy(hp->h_addr, optr, sizeof(u_long));/* hostname */
67		if (verbose)
68			fprintf(stderr, "source route to %s\n",
69							inet_ntoa(*((struct in_addr *) hp->h_addr)));
70	} else
71		err_quit("unknown host: %s\n", argptr);
72
73	optr += sizeof(u_long);		/* for next IP addr in list */
74	sroute_cnt++;
75}
76
77/*
78 * Set the actual source route with the IP_OPTIONS socket option.
79 * This function is called if srouce_cnt is nonzero.
80 * The final destination goes at the end of the list of IP addresses.
81 */
82
83void
84sroute_set(int sockfd)
85{
86	sroute_cnt++;			      /* account for destination */
87	sroute_opt[1] = 3 + (sroute_cnt * 4); /* total length, incl. destination */
88
89	/* destination must be stored as final entry */
90	bcopy(&servaddr.sin_addr, optr, sizeof(u_long));
91	optr += sizeof(u_long);
92	if (verbose) {
93		fprintf(stderr, "source route to %s\n", inet_ntoa(servaddr.sin_addr));
94		fprintf(stderr, "source route size %d bytes\n", sroute_opt[1]);
95	}
96
97	/*
98	 * The number of bytes that we pass to setsockopt() must be a multiple
99	 * of 4.  Since the buffer was initialized to 0, this leaves an EOL
100	 * following the final IP address.
101	 * For optimization we could put a NOP before the 3-byte type/len/offset
102	 * field, which would then align all the IP addresses on 4-byte boundaries,
103	 * but the source routing code is not exactly in the fast path of most
104	 * routers.
105	 */
106	while ((optr - sroute_opt) & 3)
107		optr++;
108
109	if (setsockopt(sockfd, IPPROTO_IP, IP_OPTIONS,
110		       sroute_opt, optr - sroute_opt) < 0)
111		err_sys("setsockopt error for IP_OPTIONS");
112
113	sroute_cnt = 0;		/* don't call this function again */
114}
115