1 /*
2  * fragroute.c
3  *
4  * Copyright (c) 2001 Dug Song <dugsong@monkey.org>
5  *
6  * $Id: fragroute.c,v 1.16 2002/04/07 22:55:20 dugsong Exp $
7  */
8 
9 #include "config.h"
10 
11 #include <err.h>
12 #include <signal.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <unistd.h>
17 
18 #include "pkt.h"
19 #include "mod.h"
20 #include "tun.h"
21 
22 struct fr_ctx {
23 	struct addr	 src;
24 	struct addr	 dst;
25 	struct addr	 smac;
26 	struct addr	 dmac;
27 
28 	int		 mtu;
29 
30 	arp_t		*arp;
31 	eth_t		*eth;
32 	intf_t		*intf;
33 	route_t		*route;
34 	tun_t		*tun;
35 };
36 
37 static struct fr_ctx 	 ctx;
38 
39 static void
usage(void)40 usage(void)
41 {
42 	fprintf(stderr, "Usage: fragroute [-f file] dst\n");
43 	fprintf(stderr, "Rules:\n");
44 	mod_usage();
45 	exit(1);
46 }
47 
48 #ifndef WIN32
49 #define SOCKET		int
50 #define closesocket(x)	close(x)
51 #endif
52 
53 static void _resend_outgoing(struct pkt *pkt);
54 
55 static void
_timed_outgoing(int fd,short event,void * arg)56 _timed_outgoing(int fd, short event, void *arg)
57 {
58 	struct pkt *pkt = (struct pkt *)arg;
59 
60 	memset(&pkt->pkt_ts, 0, sizeof(pkt->pkt_ts));
61 	_resend_outgoing(pkt);
62 }
63 
64 static void
_resend_outgoing(struct pkt * pkt)65 _resend_outgoing(struct pkt *pkt)
66 {
67 	if (timerisset(&pkt->pkt_ts)) {
68 		timeout_set(&pkt->pkt_ev, _timed_outgoing, pkt);
69 		timeout_add(&pkt->pkt_ev, &pkt->pkt_ts);
70 	} else {
71 		eth_pack_hdr(pkt->pkt_eth, ctx.dmac.addr_eth,
72 		    ctx.smac.addr_eth, ETH_TYPE_IP);
73 		if (eth_send(ctx.eth, pkt->pkt_data,
74 		    pkt->pkt_end - pkt->pkt_data) < 0)
75 			warn("eth_send");
76 		pkt_free(pkt);
77 	}
78 }
79 
80 static int
fragroute_close(void)81 fragroute_close(void)
82 {
83 	if (ctx.tun != NULL)	tun_close(ctx.tun);
84 	if (ctx.route != NULL)	route_close(ctx.route);
85 	if (ctx.intf != NULL)	intf_close(ctx.intf);
86 	if (ctx.eth != NULL)	eth_close(ctx.eth);
87 	if (ctx.arp != NULL)	arp_close(ctx.arp);
88 #ifdef WIN32
89 	WSACleanup();
90 #endif
91 	return (-1);
92 }
93 
94 static void
fragroute_process(void * buf,size_t len,void * arg)95 fragroute_process(void *buf, size_t len, void *arg)
96 {
97 	struct pktq pktq;
98 	struct pkt *pkt, *next;
99 
100 	if ((pkt = pkt_new()) == NULL) {
101 		warn("pkt_new");
102 		return;
103 	}
104 	if (ETH_HDR_LEN + len > PKT_BUF_LEN) {
105 		warn("dropping oversized packet");
106 		return;
107 	}
108 	memcpy(pkt->pkt_data + ETH_HDR_LEN, buf, len);
109 	pkt->pkt_end = pkt->pkt_data + ETH_HDR_LEN + len;
110 
111 	pkt_decorate(pkt);
112 
113 	if (pkt->pkt_ip == NULL) {
114 		warn("dropping non-IP packet");
115 		return;
116 	}
117 	eth_pack_hdr(pkt->pkt_eth, ctx.dmac.addr_eth,
118 	    ctx.smac.addr_eth, ETH_TYPE_IP);
119 
120 	pkt->pkt_ip->ip_src = ctx.src.addr_ip;
121 	ip_checksum(pkt->pkt_ip, len);
122 
123 	TAILQ_INIT(&pktq);
124 	TAILQ_INSERT_TAIL(&pktq, pkt, pkt_next);
125 
126 	mod_apply(&pktq);
127 
128 	for (pkt = TAILQ_FIRST(&pktq); pkt != TAILQ_END(&pktq); pkt = next) {
129 		next = TAILQ_NEXT(pkt, pkt_next);
130 		_resend_outgoing(pkt);
131 	}
132 }
133 
134 #ifdef WIN32
135 /* XXX - these should be in event.h */
136 extern int		(*event_sigcb)(void);
137 extern int		 event_gotsig;
138 
139 static BOOL CALLBACK
fragroute_signal(DWORD sig)140 fragroute_signal(DWORD sig)
141 {
142 	warnx("exiting at user request");
143 	event_gotsig++;
144 	return (TRUE);
145 }
146 #else
147 
148 static void
fragroute_signal(evutil_socket_t fd,short what,void * arg)149 fragroute_signal(evutil_socket_t fd, short what, void *arg)
150 {
151 	int sig;
152 	recv(fd, &sig, sizeof(sig), 0);
153 	warnx("exiting on signal %d", sig);
154 	exit(sig);
155 }
156 
157 static void
addsignal(int sig)158 addsignal(int sig) {
159 	struct event sig_ev;
160 	int got;
161 
162 	evsignal_set(&sig_ev, sig, fragroute_signal, &got);
163 	evsignal_add(&sig_ev, NULL);
164 }
165 
166 #endif
167 
168 static void
fragroute_init(const char * dst)169 fragroute_init(const char *dst)
170 {
171 	struct arp_entry arpent;
172 	struct intf_entry ifent;
173 	struct route_entry rtent;
174 #ifdef WIN32
175 	WSADATA wsdata;
176 
177 	if (WSAStartup(MAKEWORD(2, 2), &wsdata) != 0)
178 		err(1, "couldn't initialize Winsock");
179 
180 	SetConsoleCtrlHandler(fragroute_signal, TRUE);
181 #endif
182 	if (addr_aton(dst, &ctx.dst) < 0)
183 		err(1, "destination address invalid");
184 
185 	if (ctx.dst.addr_bits != IP_ADDR_BITS)
186 		errx(1, "only /32 destinations supported at this time");
187 
188 	pkt_init(128);
189 
190 	event_init();
191 #ifdef WIN32
192 	event_sigcb = fragroute_close;
193 #else
194 	addsignal(SIGINT);
195 	addsignal(SIGTERM);
196 #endif
197 
198 	if ((ctx.arp = arp_open()) == NULL ||
199 	    (ctx.intf = intf_open()) == NULL ||
200 	    (ctx.route = route_open()) == NULL)
201 		err(1, "couldn't open kernel networking interfaces");
202 
203 	/* Find outgoing interface, addresses, and MTU. */
204 	ifent.intf_len = sizeof(ifent);
205 	if (intf_get_dst(ctx.intf, &ifent, &ctx.dst) < 0)
206 		err(1, "couldn't determine outgoing interface");
207 
208 	memcpy(&ctx.src, &ifent.intf_addr, sizeof(ctx.src));
209 	ctx.src.addr_bits = IP_ADDR_BITS;
210 	memcpy(&ctx.smac, &ifent.intf_link_addr, sizeof(ctx.smac));
211 	ctx.mtu = ifent.intf_mtu;
212 
213 	/* Open outgoing interface for sending. */
214 	if ((ctx.eth = eth_open(ifent.intf_name)) == NULL)
215 		err(1, "couldn't open %s for sending", ifent.intf_name);
216 
217 	/* Find destination MAC address. */
218 	memcpy(&arpent.arp_pa, &ctx.dst, sizeof(arpent.arp_pa));
219 
220 	if (arp_get(ctx.arp, &arpent) < 0) {
221 		memcpy(&rtent.route_dst, &ctx.dst, sizeof(rtent.route_dst));
222 
223 		if (route_get(ctx.route, &rtent) < 0)
224 			err(1, "no route to %s", addr_ntoa(&rtent.route_dst));
225 
226 		memcpy(&arpent.arp_pa, &rtent.route_gw, sizeof(arpent.arp_pa));
227 
228 		if (arp_get(ctx.arp, &arpent) < 0)
229 			err(1, "no ARP entry for %s",
230 			    addr_ntoa(&arpent.arp_pa));
231 	}
232 	memcpy(&ctx.dmac, &arpent.arp_ha, sizeof(ctx.dmac));
233 
234 	/* Setup our tunnel. */
235 	if ((ctx.tun = tun_open(&ctx.src, &ctx.dst, ctx.mtu)) == NULL)
236 		err(1, "couldn't initialize tunnel interface");
237 
238 	tun_register(ctx.tun, fragroute_process, &ctx);
239 }
240 
241 static void
fragroute_config(char * config)242 fragroute_config(char *config)
243 {
244 	if (mod_open(config) < 0) {
245 		fragroute_close();
246 		exit(1);
247 	}
248 }
249 
250 static void
fragroute_dispatch(void)251 fragroute_dispatch(void)
252 {
253 	event_dispatch();
254 }
255 
256 int
main(int argc,char * argv[])257 main(int argc, char *argv[])
258 {
259 	extern char *optarg;
260 	extern int optind;
261 	char *conf;
262 	int c;
263 
264 	conf = FRAGROUTE_CONF;
265 
266 	while ((c = getopt(argc, argv, "f:h?")) != -1) {
267 		switch (c) {
268 		case 'f':
269 			conf = optarg;
270 			break;
271 		default:
272 			usage();
273 		}
274 	}
275 	argc -= optind;
276 	argv += optind;
277 
278 	if (argc != 1)
279 		usage();
280 
281 	fragroute_init(argv[0]);
282 	fragroute_config(conf);
283 	fragroute_dispatch();
284 
285 	exit(0);
286 }
287