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