]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - usr.sbin/ppp/nat_cmd.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.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$
28  */
29
30 #include <sys/param.h>
31 #include <netinet/in.h>
32 #include <arpa/inet.h>
33 #include <netdb.h>
34 #include <netinet/in_systm.h>
35 #include <netinet/in.h>
36 #include <netinet/ip.h>
37 #include <sys/socket.h>
38 #include <sys/un.h>
39
40 #include <stdarg.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <termios.h>
45
46 #ifdef LOCALNAT
47 #include "alias.h"
48 #else
49 #include <alias.h>
50 #endif
51
52 #include "layer.h"
53 #include "proto.h"
54 #include "defs.h"
55 #include "command.h"
56 #include "log.h"
57 #include "nat_cmd.h"
58 #include "descriptor.h"
59 #include "prompt.h"
60 #include "timer.h"
61 #include "fsm.h"
62 #include "slcompress.h"
63 #include "throughput.h"
64 #include "iplist.h"
65 #include "mbuf.h"
66 #include "lqr.h"
67 #include "hdlc.h"
68 #include "ncpaddr.h"
69 #include "ip.h"
70 #include "ipcp.h"
71 #include "ipv6cp.h"
72 #include "lcp.h"
73 #include "ccp.h"
74 #include "link.h"
75 #include "mp.h"
76 #include "filter.h"
77 #ifndef NORADIUS
78 #include "radius.h"
79 #endif
80 #include "ncp.h"
81 #include "bundle.h"
82
83
84 #define NAT_EXTRABUF (13)
85
86 static int StrToAddr(const char *, struct in_addr *);
87 static int StrToPortRange(const char *, u_short *, u_short *, const char *);
88 static int StrToAddrAndPort(const char *, struct in_addr *, u_short *,
89                             u_short *, const char *);
90
91 extern struct libalias *la;
92
93 static void
94 lowhigh(u_short *a, u_short *b)
95 {
96   if (a > b) {
97     u_short c;
98
99     c = *b;
100     *b = *a;
101     *a = c;
102   }
103 }
104
105 int
106 nat_RedirectPort(struct cmdargs const *arg)
107 {
108   if (!arg->bundle->NatEnabled) {
109     prompt_Printf(arg->prompt, "Alias not enabled\n");
110     return 1;
111   } else if (arg->argc == arg->argn + 3 || arg->argc == arg->argn + 4) {
112     char proto_constant;
113     const char *proto;
114     struct in_addr localaddr;
115     u_short hlocalport, llocalport;
116     struct in_addr aliasaddr;
117     u_short haliasport, laliasport;
118     struct in_addr remoteaddr;
119     u_short hremoteport, lremoteport;
120     struct alias_link *link;
121     int error;
122
123     proto = arg->argv[arg->argn];
124     if (strcmp(proto, "tcp") == 0) {
125       proto_constant = IPPROTO_TCP;
126     } else if (strcmp(proto, "udp") == 0) {
127       proto_constant = IPPROTO_UDP;
128     } else {
129       prompt_Printf(arg->prompt, "port redirect: protocol must be"
130                     " tcp or udp\n");
131       return -1;
132     }
133
134     error = StrToAddrAndPort(arg->argv[arg->argn+1], &localaddr, &llocalport,
135                              &hlocalport, proto);
136     if (error) {
137       prompt_Printf(arg->prompt, "nat port: error reading localaddr:port\n");
138       return -1;
139     }
140
141     error = StrToPortRange(arg->argv[arg->argn+2], &laliasport, &haliasport,
142                            proto);
143     if (error) {
144       prompt_Printf(arg->prompt, "nat port: error reading alias port\n");
145       return -1;
146     }
147     aliasaddr.s_addr = INADDR_ANY;
148
149     if (arg->argc == arg->argn + 4) {
150       error = StrToAddrAndPort(arg->argv[arg->argn+3], &remoteaddr,
151                                &lremoteport, &hremoteport, proto);
152       if (error) {
153         prompt_Printf(arg->prompt, "nat port: error reading "
154                       "remoteaddr:port\n");
155         return -1;
156       }
157     } else {
158       remoteaddr.s_addr = INADDR_ANY;
159       lremoteport = hremoteport = 0;
160     }
161
162     lowhigh(&llocalport, &hlocalport);
163     lowhigh(&laliasport, &haliasport);
164     lowhigh(&lremoteport, &hremoteport);
165
166     if (haliasport - laliasport != hlocalport - llocalport) {
167       prompt_Printf(arg->prompt, "nat port: local & alias port ranges "
168                     "are not equal\n");
169       return -1;
170     }
171
172     if (hremoteport && hremoteport - lremoteport != hlocalport - llocalport) {
173       prompt_Printf(arg->prompt, "nat port: local & remote port ranges "
174                     "are not equal\n");
175       return -1;
176     }
177
178     do {
179       link = LibAliasRedirectPort(la, localaddr, htons(llocalport),
180                                      remoteaddr, htons(lremoteport),
181                                      aliasaddr, htons(laliasport),
182                                      proto_constant);
183
184       if (link == NULL) {
185         prompt_Printf(arg->prompt, "nat port: %d: error %d\n", laliasport,
186                       error);
187         return 1;
188       }
189       llocalport++;
190       if (hremoteport)
191         lremoteport++;
192     } while (laliasport++ < haliasport);
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 = LibAliasRedirectAddr(la, 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;
249     unsigned len;
250
251     len = strlen(arg->argv[arg->argn]);
252     if (len == 0) {
253       prompt_Printf(arg->prompt, "proto redirect: invalid protocol\n");
254       return 1;
255     }
256     if (strspn(arg->argv[arg->argn], "01234567") == len)
257       pe = getprotobynumber(atoi(arg->argv[arg->argn]));
258     else
259       pe = getprotobyname(arg->argv[arg->argn]);
260     if (pe == NULL) {
261       prompt_Printf(arg->prompt, "proto redirect: invalid protocol\n");
262       return 1;
263     }
264
265     error = StrToAddr(arg->argv[arg->argn + 1], &localIP);
266     if (error) {
267       prompt_Printf(arg->prompt, "proto redirect: invalid src address\n");
268       return 1;
269     }
270
271     if (arg->argc >= arg->argn + 3) {
272       error = StrToAddr(arg->argv[arg->argn + 2], &publicIP);
273       if (error) {
274         prompt_Printf(arg->prompt, "proto redirect: invalid alias address\n");
275         prompt_Printf(arg->prompt, "usage: nat %s %s\n", arg->cmd->name,
276                       arg->cmd->syntax);
277         return 1;
278       }
279     } else
280       publicIP.s_addr = INADDR_ANY;
281
282     if (arg->argc == arg->argn + 4) {
283       error = StrToAddr(arg->argv[arg->argn + 2], &remoteIP);
284       if (error) {
285         prompt_Printf(arg->prompt, "proto redirect: invalid dst address\n");
286         prompt_Printf(arg->prompt, "usage: nat %s %s\n", arg->cmd->name,
287                       arg->cmd->syntax);
288         return 1;
289       }
290     } else
291       remoteIP.s_addr = INADDR_ANY;
292
293     link = LibAliasRedirectProto(la, localIP, remoteIP, publicIP, pe->p_proto);
294     if (link == NULL) {
295       prompt_Printf(arg->prompt, "proto redirect: packet aliasing"
296                     " engine error\n");
297       prompt_Printf(arg->prompt, "usage: nat %s %s\n", arg->cmd->name,
298                     arg->cmd->syntax);
299     }
300   } else
301     return -1;
302
303   return 0;
304 }
305
306
307 static int
308 StrToAddr(const char *str, struct in_addr *addr)
309 {
310   struct hostent *hp;
311
312   if (inet_aton(str, addr))
313     return 0;
314
315   hp = gethostbyname(str);
316   if (!hp) {
317     log_Printf(LogWARN, "StrToAddr: Unknown host %s.\n", str);
318     return -1;
319   }
320   *addr = *((struct in_addr *) hp->h_addr);
321   return 0;
322 }
323
324
325 static int
326 StrToPort(const char *str, u_short *port, const char *proto)
327 {
328   struct servent *sp;
329   char *end;
330
331   *port = strtol(str, &end, 10);
332   if (*end != '\0') {
333     sp = getservbyname(str, proto);
334     if (sp == NULL) {
335       log_Printf(LogWARN, "StrToAddr: Unknown port or service %s/%s.\n",
336                 str, proto);
337       return -1;
338     }
339     *port = ntohs(sp->s_port);
340   }
341
342   return 0;
343 }
344
345 static int
346 StrToPortRange(const char *str, u_short *low, u_short *high, const char *proto)
347 {
348   char *minus;
349   int res;
350
351   minus = strchr(str, '-');
352   if (minus)
353     *minus = '\0';              /* Cheat the const-ness ! */
354
355   res = StrToPort(str, low, proto);
356
357   if (minus)
358     *minus = '-';               /* Cheat the const-ness ! */
359
360   if (res == 0) {
361     if (minus)
362       res = StrToPort(minus + 1, high, proto);
363     else
364       *high = *low;
365   }
366
367   return res;
368 }
369
370 static int
371 StrToAddrAndPort(const char *str, struct in_addr *addr, u_short *low,
372                  u_short *high, const char *proto)
373 {
374   char *colon;
375   int res;
376
377   colon = strchr(str, ':');
378   if (!colon) {
379     log_Printf(LogWARN, "StrToAddrAndPort: %s is missing port number.\n", str);
380     return -1;
381   }
382
383   *colon = '\0';                /* Cheat the const-ness ! */
384   res = StrToAddr(str, addr);
385   *colon = ':';                 /* Cheat the const-ness ! */
386   if (res != 0)
387     return -1;
388
389   return StrToPortRange(colon + 1, low, high, proto);
390 }
391
392 int
393 nat_ProxyRule(struct cmdargs const *arg)
394 {
395   char cmd[LINE_LEN];
396   int f, pos;
397   size_t len;
398
399   if (arg->argn >= arg->argc)
400     return -1;
401
402   for (f = arg->argn, pos = 0; f < arg->argc; f++) {
403     len = strlen(arg->argv[f]);
404     if (sizeof cmd - pos < len + (len ? 1 : 0))
405       break;
406     if (len)
407       cmd[pos++] = ' ';
408     strcpy(cmd + pos, arg->argv[f]);
409     pos += len;
410   }
411
412   return LibAliasProxyRule(la, cmd);
413 }
414
415 int
416 nat_SetTarget(struct cmdargs const *arg)
417 {
418   struct in_addr addr;
419
420   if (arg->argc == arg->argn) {
421     addr.s_addr = INADDR_ANY;
422     LibAliasSetTarget(la, addr);
423     return 0;
424   }
425
426   if (arg->argc != arg->argn + 1)
427     return -1;
428
429   if (!strcasecmp(arg->argv[arg->argn], "MYADDR")) {
430     addr.s_addr = INADDR_ANY;
431     LibAliasSetTarget(la, addr);
432     return 0;
433   }
434
435   addr = GetIpAddr(arg->argv[arg->argn]);
436   if (addr.s_addr == INADDR_NONE) {
437     log_Printf(LogWARN, "%s: invalid address\n", arg->argv[arg->argn]);
438     return 1;
439   }
440
441   LibAliasSetTarget(la, addr);
442   return 0;
443 }
444
445 #ifndef NO_FW_PUNCH
446 int
447 nat_PunchFW(struct cmdargs const *arg)
448 {
449   char *end;
450   long base, count;
451
452   if (arg->argc == arg->argn) {
453     LibAliasSetMode(la, 0, PKT_ALIAS_PUNCH_FW);
454     return 0;
455   }
456
457   if (arg->argc != arg->argn + 2)
458     return -1;
459
460   base = strtol(arg->argv[arg->argn], &end, 10);
461   if (*end != '\0' || base < 0)
462     return -1;
463
464   count = strtol(arg->argv[arg->argn + 1], &end, 10);
465   if (*end != '\0' || count < 0)
466     return -1;
467
468   LibAliasSetFWBase(la, base, count);
469   LibAliasSetMode(la, PKT_ALIAS_PUNCH_FW, PKT_ALIAS_PUNCH_FW);
470
471   return 0;
472 }
473 #endif
474
475 int
476 nat_SkinnyPort(struct cmdargs const *arg)
477 {
478   char *end;
479   long port;
480
481   if (arg->argc == arg->argn) {
482     LibAliasSetSkinnyPort(la, 0);
483     return 0;
484   }
485
486   if (arg->argc != arg->argn + 1)
487     return -1;
488
489   port = strtol(arg->argv[arg->argn], &end, 10);
490   if (*end != '\0' || port < 0)
491     return -1;
492
493   LibAliasSetSkinnyPort(la, port);
494
495   return 0;
496 }
497
498 static struct mbuf *
499 nat_LayerPush(struct bundle *bundle, struct link *l __unused, struct mbuf *bp,
500                 int pri __unused, u_short *proto)
501 {
502   if (!bundle->NatEnabled || *proto != PROTO_IP)
503     return bp;
504
505   log_Printf(LogDEBUG, "nat_LayerPush: PROTO_IP -> PROTO_IP\n");
506   m_settype(bp, MB_NATOUT);
507   /* Ensure there's a bit of extra buffer for the NAT code... */
508   bp = m_pullup(m_append(bp, NULL, NAT_EXTRABUF));
509   LibAliasOut(la, MBUF_CTOP(bp), bp->m_len);
510   bp->m_len = ntohs(((struct ip *)MBUF_CTOP(bp))->ip_len);
511
512   return bp;
513 }
514
515 static struct mbuf *
516 nat_LayerPull(struct bundle *bundle, struct link *l __unused, struct mbuf *bp,
517                 u_short *proto)
518 {
519   static int gfrags;
520   int ret, len, nfrags;
521   struct mbuf **last;
522   char *fptr;
523
524   if (!bundle->NatEnabled || *proto != PROTO_IP)
525     return bp;
526
527   log_Printf(LogDEBUG, "nat_LayerPull: PROTO_IP -> PROTO_IP\n");
528   m_settype(bp, MB_NATIN);
529   /* Ensure there's a bit of extra buffer for the NAT code... */
530   bp = m_pullup(m_append(bp, NULL, NAT_EXTRABUF));
531   ret = LibAliasIn(la, MBUF_CTOP(bp), bp->m_len);
532
533   bp->m_len = ntohs(((struct ip *)MBUF_CTOP(bp))->ip_len);
534   if (bp->m_len > MAX_MRU) {
535     log_Printf(LogWARN, "nat_LayerPull: Problem with IP header length (%lu)\n",
536                (unsigned long)bp->m_len);
537     m_freem(bp);
538     return NULL;
539   }
540
541   switch (ret) {
542     case PKT_ALIAS_OK:
543       break;
544
545     case PKT_ALIAS_UNRESOLVED_FRAGMENT:
546       /* Save the data for later */
547       if ((fptr = malloc(bp->m_len)) == NULL) {
548         log_Printf(LogWARN, "nat_LayerPull: Dropped unresolved fragment -"
549                    " out of memory!\n");
550         m_freem(bp);
551         bp = NULL;
552       } else {
553         bp = mbuf_Read(bp, fptr, bp->m_len);
554         LibAliasSaveFragment(la, fptr);
555         log_Printf(LogDEBUG, "Store another frag (%lu) - now %d\n",
556                    (unsigned long)((struct ip *)fptr)->ip_id, ++gfrags);
557       }
558       break;
559
560     case PKT_ALIAS_FOUND_HEADER_FRAGMENT:
561       /* Fetch all the saved fragments and chain them on the end of `bp' */
562       last = &bp->m_nextpkt;
563       nfrags = 0;
564       while ((fptr = LibAliasGetFragment(la, MBUF_CTOP(bp))) != NULL) {
565         nfrags++;
566         LibAliasFragmentIn(la, MBUF_CTOP(bp), fptr);
567         len = ntohs(((struct ip *)fptr)->ip_len);
568         *last = m_get(len, MB_NATIN);
569         memcpy(MBUF_CTOP(*last), fptr, len);
570         free(fptr);
571         last = &(*last)->m_nextpkt;
572       }
573       gfrags -= nfrags;
574       log_Printf(LogDEBUG, "Found a frag header (%lu) - plus %d more frags (no"
575                  "w %d)\n", (unsigned long)((struct ip *)MBUF_CTOP(bp))->ip_id,
576                  nfrags, gfrags);
577       break;
578
579     case PKT_ALIAS_IGNORED:
580       if (LibAliasSetMode(la, 0, 0) & PKT_ALIAS_DENY_INCOMING) {
581         log_Printf(LogTCPIP, "NAT engine denied data:\n");
582         m_freem(bp);
583         bp = NULL;
584       } else if (log_IsKept(LogTCPIP)) {
585         log_Printf(LogTCPIP, "NAT engine ignored data:\n");
586         PacketCheck(bundle, AF_INET, MBUF_CTOP(bp), bp->m_len, NULL,
587                     NULL, NULL);
588       }
589       break;
590
591     default:
592       log_Printf(LogWARN, "nat_LayerPull: Dropped a packet (%d)....\n", ret);
593       m_freem(bp);
594       bp = NULL;
595       break;
596   }
597
598   return bp;
599 }
600
601 struct layer natlayer =
602   { LAYER_NAT, "nat", nat_LayerPush, nat_LayerPull };