Merge commit 'crater/master'
[dragonfly.git] / usr.sbin / ppp / nat_cmd.c
1 /*-
2  * Copyright (c) 2001 Charles Mott <cm@linktel.net>
3  *                    Brian Somers <brian@Awfulhak.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  * $FreeBSD: src/usr.sbin/ppp/nat_cmd.c,v 1.35.2.13 2002/09/01 02:12:29 brian Exp $
28  * $DragonFly: src/usr.sbin/ppp/nat_cmd.c,v 1.2 2003/06/17 04:30:00 dillon Exp $
29  */
30
31 #include <sys/param.h>
32 #include <netinet/in.h>
33 #include <arpa/inet.h>
34 #include <netdb.h>
35 #include <netinet/in_systm.h>
36 #include <netinet/in.h>
37 #include <netinet/ip.h>
38 #include <sys/socket.h>
39 #include <sys/un.h>
40
41 #include <stdarg.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <termios.h>
46
47 #ifdef LOCALNAT
48 #include "alias.h"
49 #else
50 #include <alias.h>
51 #endif
52
53 #include "layer.h"
54 #include "proto.h"
55 #include "defs.h"
56 #include "command.h"
57 #include "log.h"
58 #include "nat_cmd.h"
59 #include "descriptor.h"
60 #include "prompt.h"
61 #include "timer.h"
62 #include "fsm.h"
63 #include "slcompress.h"
64 #include "throughput.h"
65 #include "iplist.h"
66 #include "mbuf.h"
67 #include "lqr.h"
68 #include "hdlc.h"
69 #include "ncpaddr.h"
70 #include "ip.h"
71 #include "ipcp.h"
72 #include "ipv6cp.h"
73 #include "lcp.h"
74 #include "ccp.h"
75 #include "link.h"
76 #include "mp.h"
77 #include "filter.h"
78 #ifndef NORADIUS
79 #include "radius.h"
80 #endif
81 #include "ncp.h"
82 #include "bundle.h"
83
84
85 #define NAT_EXTRABUF (13)
86
87 static int StrToAddr(const char *, struct in_addr *);
88 static int StrToPortRange(const char *, u_short *, u_short *, const char *);
89 static int StrToAddrAndPort(const char *, struct in_addr *, u_short *,
90                             u_short *, const char *);
91
92 static void
93 lowhigh(u_short *a, u_short *b)
94 {
95   if (a > b) {
96     u_short c;
97
98     c = *b;
99     *b = *a;
100     *a = c;
101   }
102 }
103
104 int
105 nat_RedirectPort(struct cmdargs const *arg)
106 {
107   if (!arg->bundle->NatEnabled) {
108     prompt_Printf(arg->prompt, "Alias not enabled\n");
109     return 1;
110   } else if (arg->argc == arg->argn + 3 || arg->argc == arg->argn + 4) {
111     char proto_constant;
112     const char *proto;
113     struct in_addr localaddr;
114     u_short hlocalport, llocalport;
115     struct in_addr aliasaddr;
116     u_short haliasport, laliasport;
117     struct in_addr remoteaddr;
118     u_short hremoteport, lremoteport;
119     struct alias_link *link;
120     int error;
121
122     proto = arg->argv[arg->argn];
123     if (strcmp(proto, "tcp") == 0) {
124       proto_constant = IPPROTO_TCP;
125     } else if (strcmp(proto, "udp") == 0) {
126       proto_constant = IPPROTO_UDP;
127     } else {
128       prompt_Printf(arg->prompt, "port redirect: protocol must be"
129                     " tcp or udp\n");
130       return -1;
131     }
132
133     error = StrToAddrAndPort(arg->argv[arg->argn+1], &localaddr, &llocalport,
134                              &hlocalport, proto);
135     if (error) {
136       prompt_Printf(arg->prompt, "nat port: error reading localaddr:port\n");
137       return -1;
138     }
139
140     error = StrToPortRange(arg->argv[arg->argn+2], &laliasport, &haliasport,
141                            proto);
142     if (error) {
143       prompt_Printf(arg->prompt, "nat port: error reading alias port\n");
144       return -1;
145     }
146     aliasaddr.s_addr = INADDR_ANY;
147
148     if (arg->argc == arg->argn + 4) {
149       error = StrToAddrAndPort(arg->argv[arg->argn+3], &remoteaddr,
150                                &lremoteport, &hremoteport, proto);
151       if (error) {
152         prompt_Printf(arg->prompt, "nat port: error reading "
153                       "remoteaddr:port\n");
154         return -1;
155       }
156     } else {
157       remoteaddr.s_addr = INADDR_ANY;
158       lremoteport = hremoteport = 0;
159     }
160
161     lowhigh(&llocalport, &hlocalport);
162     lowhigh(&laliasport, &haliasport);
163     lowhigh(&lremoteport, &hremoteport);
164
165     if (haliasport - laliasport != hlocalport - llocalport) {
166       prompt_Printf(arg->prompt, "nat port: local & alias port ranges "
167                     "are not equal\n");
168       return -1;
169     }
170
171     if (hremoteport && hremoteport - lremoteport != hlocalport - llocalport) {
172       prompt_Printf(arg->prompt, "nat port: local & remote port ranges "
173                     "are not equal\n");
174       return -1;
175     }
176
177     while (laliasport <= haliasport) {
178       link = PacketAliasRedirectPort(localaddr, htons(llocalport),
179                                      remoteaddr, htons(lremoteport),
180                                      aliasaddr, htons(laliasport),
181                                      proto_constant);
182
183       if (link == NULL) {
184         prompt_Printf(arg->prompt, "nat port: %d: error %d\n", laliasport,
185                       error);
186         return 1;
187       }
188       llocalport++;
189       laliasport++;
190       if (hremoteport)
191         lremoteport++;
192     }
193
194     return 0;
195   }
196
197   return -1;
198 }
199
200
201 int
202 nat_RedirectAddr(struct cmdargs const *arg)
203 {
204   if (!arg->bundle->NatEnabled) {
205     prompt_Printf(arg->prompt, "nat not enabled\n");
206     return 1;
207   } else if (arg->argc == arg->argn+2) {
208     int error;
209     struct in_addr localaddr, aliasaddr;
210     struct alias_link *link;
211
212     error = StrToAddr(arg->argv[arg->argn], &localaddr);
213     if (error) {
214       prompt_Printf(arg->prompt, "address redirect: invalid local address\n");
215       return 1;
216     }
217     error = StrToAddr(arg->argv[arg->argn+1], &aliasaddr);
218     if (error) {
219       prompt_Printf(arg->prompt, "address redirect: invalid alias address\n");
220       prompt_Printf(arg->prompt, "usage: nat %s %s\n", arg->cmd->name,
221                     arg->cmd->syntax);
222       return 1;
223     }
224     link = PacketAliasRedirectAddr(localaddr, aliasaddr);
225     if (link == NULL) {
226       prompt_Printf(arg->prompt, "address redirect: packet aliasing"
227                     " engine error\n");
228       prompt_Printf(arg->prompt, "usage: nat %s %s\n", arg->cmd->name,
229                     arg->cmd->syntax);
230     }
231   } else
232     return -1;
233
234   return 0;
235 }
236
237
238 int
239 nat_RedirectProto(struct cmdargs const *arg)
240 {
241   if (!arg->bundle->NatEnabled) {
242     prompt_Printf(arg->prompt, "nat not enabled\n");
243     return 1;
244   } else if (arg->argc >= arg->argn + 2 && arg->argc <= arg->argn + 4) {
245     struct in_addr localIP, publicIP, remoteIP;
246     struct alias_link *link;
247     struct protoent *pe;
248     int error, len;
249
250     len = strlen(arg->argv[arg->argn]);
251     if (len == 0) {
252       prompt_Printf(arg->prompt, "proto redirect: invalid protocol\n");
253       return 1;
254     }
255     if (strspn(arg->argv[arg->argn], "01234567") == len)
256       pe = getprotobynumber(atoi(arg->argv[arg->argn]));
257     else
258       pe = getprotobyname(arg->argv[arg->argn]);
259     if (pe == NULL) {
260       prompt_Printf(arg->prompt, "proto redirect: invalid protocol\n");
261       return 1;
262     }
263
264     error = StrToAddr(arg->argv[arg->argn + 1], &localIP);
265     if (error) {
266       prompt_Printf(arg->prompt, "proto redirect: invalid src address\n");
267       return 1;
268     }
269
270     if (arg->argc >= arg->argn + 3) {
271       error = StrToAddr(arg->argv[arg->argn + 2], &publicIP);
272       if (error) {
273         prompt_Printf(arg->prompt, "proto redirect: invalid alias address\n");
274         prompt_Printf(arg->prompt, "usage: nat %s %s\n", arg->cmd->name,
275                       arg->cmd->syntax);
276         return 1;
277       }
278     } else
279       publicIP.s_addr = INADDR_ANY;
280
281     if (arg->argc == arg->argn + 4) {
282       error = StrToAddr(arg->argv[arg->argn + 2], &remoteIP);
283       if (error) {
284         prompt_Printf(arg->prompt, "proto redirect: invalid dst address\n");
285         prompt_Printf(arg->prompt, "usage: nat %s %s\n", arg->cmd->name,
286                       arg->cmd->syntax);
287         return 1;
288       }
289     } else
290       remoteIP.s_addr = INADDR_ANY;
291
292     link = PacketAliasRedirectProto(localIP, remoteIP, publicIP, pe->p_proto);
293     if (link == NULL) {
294       prompt_Printf(arg->prompt, "proto redirect: packet aliasing"
295                     " engine error\n");
296       prompt_Printf(arg->prompt, "usage: nat %s %s\n", arg->cmd->name,
297                     arg->cmd->syntax);
298     }
299   } else
300     return -1;
301
302   return 0;
303 }
304
305
306 static int
307 StrToAddr(const char *str, struct in_addr *addr)
308 {
309   struct hostent *hp;
310
311   if (inet_aton(str, addr))
312     return 0;
313
314   hp = gethostbyname(str);
315   if (!hp) {
316     log_Printf(LogWARN, "StrToAddr: Unknown host %s.\n", str);
317     return -1;
318   }
319   *addr = *((struct in_addr *) hp->h_addr);
320   return 0;
321 }
322
323
324 static int
325 StrToPort(const char *str, u_short *port, const char *proto)
326 {
327   struct servent *sp;
328   char *end;
329
330   *port = strtol(str, &end, 10);
331   if (*end != '\0') {
332     sp = getservbyname(str, proto);
333     if (sp == NULL) {
334       log_Printf(LogWARN, "StrToAddr: Unknown port or service %s/%s.\n",
335                 str, proto);
336       return -1;
337     }
338     *port = ntohs(sp->s_port);
339   }
340
341   return 0;
342 }
343
344 static int
345 StrToPortRange(const char *str, u_short *low, u_short *high, const char *proto)
346 {
347   char *minus;
348   int res;
349
350   minus = strchr(str, '-');
351   if (minus)
352     *minus = '\0';              /* Cheat the const-ness ! */
353
354   res = StrToPort(str, low, proto);
355
356   if (minus)
357     *minus = '-';               /* Cheat the const-ness ! */
358
359   if (res == 0) {
360     if (minus)
361       res = StrToPort(minus + 1, high, proto);
362     else
363       *high = *low;
364   }
365
366   return res;
367 }
368
369 static int
370 StrToAddrAndPort(const char *str, struct in_addr *addr, u_short *low,
371                  u_short *high, const char *proto)
372 {
373   char *colon;
374   int res;
375
376   colon = strchr(str, ':');
377   if (!colon) {
378     log_Printf(LogWARN, "StrToAddrAndPort: %s is missing port number.\n", str);
379     return -1;
380   }
381
382   *colon = '\0';                /* Cheat the const-ness ! */
383   res = StrToAddr(str, addr);
384   *colon = ':';                 /* Cheat the const-ness ! */
385   if (res != 0)
386     return -1;
387
388   return StrToPortRange(colon + 1, low, high, proto);
389 }
390
391 int
392 nat_ProxyRule(struct cmdargs const *arg)
393 {
394   char cmd[LINE_LEN];
395   int f, pos;
396   size_t len;
397
398   if (arg->argn >= arg->argc)
399     return -1;
400
401   for (f = arg->argn, pos = 0; f < arg->argc; f++) {
402     len = strlen(arg->argv[f]);
403     if (sizeof cmd - pos < len + (len ? 1 : 0))
404       break;
405     if (len)
406       cmd[pos++] = ' ';
407     strcpy(cmd + pos, arg->argv[f]);
408     pos += len;
409   }
410
411   return PacketAliasProxyRule(cmd);
412 }
413
414 int
415 nat_SetTarget(struct cmdargs const *arg)
416 {
417   struct in_addr addr;
418
419   if (arg->argc == arg->argn) {
420     addr.s_addr = INADDR_ANY;
421     PacketAliasSetTarget(addr);
422     return 0;
423   }
424
425   if (arg->argc != arg->argn + 1)
426     return -1;
427
428   if (!strcasecmp(arg->argv[arg->argn], "MYADDR")) {
429     addr.s_addr = INADDR_ANY;
430     PacketAliasSetTarget(addr);
431     return 0;
432   }
433
434   addr = GetIpAddr(arg->argv[arg->argn]);
435   if (addr.s_addr == INADDR_NONE) {
436     log_Printf(LogWARN, "%s: invalid address\n", arg->argv[arg->argn]);
437     return 1;
438   }
439
440   PacketAliasSetTarget(addr);
441   return 0;
442 }
443
444 #ifndef NO_FW_PUNCH
445 int
446 nat_PunchFW(struct cmdargs const *arg)
447 {
448   char *end;
449   long base, count;
450
451   if (arg->argc == arg->argn) {
452     PacketAliasSetMode(0, PKT_ALIAS_PUNCH_FW);
453     return 0;
454   }
455
456   if (arg->argc != arg->argn + 2)
457     return -1;
458
459   base = strtol(arg->argv[arg->argn], &end, 10);
460   if (*end != '\0' || base < 0)
461     return -1;
462
463   count = strtol(arg->argv[arg->argn + 1], &end, 10);
464   if (*end != '\0' || count < 0)
465     return -1;
466
467   PacketAliasSetFWBase(base, count);
468   PacketAliasSetMode(PKT_ALIAS_PUNCH_FW, PKT_ALIAS_PUNCH_FW);
469
470   return 0;
471 }
472 #endif
473
474 static struct mbuf *
475 nat_LayerPush(struct bundle *bundle, struct link *l, struct mbuf *bp,
476                 int pri, u_short *proto)
477 {
478   if (!bundle->NatEnabled || *proto != PROTO_IP)
479     return bp;
480
481   log_Printf(LogDEBUG, "nat_LayerPush: PROTO_IP -> PROTO_IP\n");
482   m_settype(bp, MB_NATOUT);
483   /* Ensure there's a bit of extra buffer for the NAT code... */
484   bp = m_pullup(m_append(bp, NULL, NAT_EXTRABUF));
485   PacketAliasOut(MBUF_CTOP(bp), bp->m_len);
486   bp->m_len = ntohs(((struct ip *)MBUF_CTOP(bp))->ip_len);
487
488   return bp;
489 }
490
491 static struct mbuf *
492 nat_LayerPull(struct bundle *bundle, struct link *l, struct mbuf *bp,
493                 u_short *proto)
494 {
495   static int gfrags;
496   int ret, len, nfrags;
497   struct mbuf **last;
498   char *fptr;
499
500   if (!bundle->NatEnabled || *proto != PROTO_IP)
501     return bp;
502
503   log_Printf(LogDEBUG, "nat_LayerPull: PROTO_IP -> PROTO_IP\n");
504   m_settype(bp, MB_NATIN);
505   /* Ensure there's a bit of extra buffer for the NAT code... */
506   bp = m_pullup(m_append(bp, NULL, NAT_EXTRABUF));
507   ret = PacketAliasIn(MBUF_CTOP(bp), bp->m_len);
508
509   bp->m_len = ntohs(((struct ip *)MBUF_CTOP(bp))->ip_len);
510   if (bp->m_len > MAX_MRU) {
511     log_Printf(LogWARN, "nat_LayerPull: Problem with IP header length (%lu)\n",
512                (unsigned long)bp->m_len);
513     m_freem(bp);
514     return NULL;
515   }
516
517   switch (ret) {
518     case PKT_ALIAS_OK:
519       break;
520
521     case PKT_ALIAS_UNRESOLVED_FRAGMENT:
522       /* Save the data for later */
523       fptr = malloc(bp->m_len);
524       bp = mbuf_Read(bp, fptr, bp->m_len);
525       PacketAliasSaveFragment(fptr);
526       log_Printf(LogDEBUG, "Store another frag (%lu) - now %d\n",
527                  (unsigned long)((struct ip *)fptr)->ip_id, ++gfrags);
528       break;
529
530     case PKT_ALIAS_FOUND_HEADER_FRAGMENT:
531       /* Fetch all the saved fragments and chain them on the end of `bp' */
532       last = &bp->m_nextpkt;
533       nfrags = 0;
534       while ((fptr = PacketAliasGetFragment(MBUF_CTOP(bp))) != NULL) {
535         nfrags++;
536         PacketAliasFragmentIn(MBUF_CTOP(bp), fptr);
537         len = ntohs(((struct ip *)fptr)->ip_len);
538         *last = m_get(len, MB_NATIN);
539         memcpy(MBUF_CTOP(*last), fptr, len);
540         free(fptr);
541         last = &(*last)->m_nextpkt;
542       }
543       gfrags -= nfrags;
544       log_Printf(LogDEBUG, "Found a frag header (%lu) - plus %d more frags (no"
545                  "w %d)\n", (unsigned long)((struct ip *)MBUF_CTOP(bp))->ip_id,
546                  nfrags, gfrags);
547       break;
548
549     case PKT_ALIAS_IGNORED:
550       if (PacketAliasSetMode(0, 0) & PKT_ALIAS_DENY_INCOMING) {
551         log_Printf(LogTCPIP, "NAT engine denied data:\n");
552         m_freem(bp);
553         bp = NULL;
554       } else if (log_IsKept(LogTCPIP)) {
555         log_Printf(LogTCPIP, "NAT engine ignored data:\n");
556         PacketCheck(bundle, AF_INET, MBUF_CTOP(bp), bp->m_len, NULL,
557                     NULL, NULL);
558       }
559       break;
560
561     default:
562       log_Printf(LogWARN, "nat_LayerPull: Dropped a packet (%d)....\n", ret);
563       m_freem(bp);
564       bp = NULL;
565       break;
566   }
567
568   return bp;
569 }
570
571 struct layer natlayer =
572   { LAYER_NAT, "nat", nat_LayerPush, nat_LayerPull };