xref: /freebsd/usr.sbin/ppp/nat_cmd.c (revision c6ec7d31830ab1c80edae95ad5e4b9dba10c47ac)
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/ip.h>
36 #include <sys/socket.h>
37 #include <sys/un.h>
38 
39 #include <stdarg.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <termios.h>
44 
45 #ifdef LOCALNAT
46 #include "alias.h"
47 #else
48 #include <alias.h>
49 #endif
50 
51 #include "layer.h"
52 #include "proto.h"
53 #include "defs.h"
54 #include "command.h"
55 #include "log.h"
56 #include "nat_cmd.h"
57 #include "descriptor.h"
58 #include "prompt.h"
59 #include "timer.h"
60 #include "fsm.h"
61 #include "slcompress.h"
62 #include "throughput.h"
63 #include "iplist.h"
64 #include "mbuf.h"
65 #include "lqr.h"
66 #include "hdlc.h"
67 #include "ncpaddr.h"
68 #include "ip.h"
69 #include "ipcp.h"
70 #include "ipv6cp.h"
71 #include "lcp.h"
72 #include "ccp.h"
73 #include "link.h"
74 #include "mp.h"
75 #include "filter.h"
76 #ifndef NORADIUS
77 #include "radius.h"
78 #endif
79 #include "ncp.h"
80 #include "bundle.h"
81 
82 
83 #define NAT_EXTRABUF (13)
84 
85 static int StrToAddr(const char *, struct in_addr *);
86 static int StrToPortRange(const char *, u_short *, u_short *, const char *);
87 static int StrToAddrAndPort(const char *, struct in_addr *, u_short *,
88                             u_short *, const char *);
89 
90 extern struct libalias *la;
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     do {
178       link = LibAliasRedirectPort(la, 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       if (hremoteport)
190         lremoteport++;
191     } while (laliasport++ < haliasport);
192 
193     return 0;
194   }
195 
196   return -1;
197 }
198 
199 
200 int
201 nat_RedirectAddr(struct cmdargs const *arg)
202 {
203   if (!arg->bundle->NatEnabled) {
204     prompt_Printf(arg->prompt, "nat not enabled\n");
205     return 1;
206   } else if (arg->argc == arg->argn+2) {
207     int error;
208     struct in_addr localaddr, aliasaddr;
209     struct alias_link *link;
210 
211     error = StrToAddr(arg->argv[arg->argn], &localaddr);
212     if (error) {
213       prompt_Printf(arg->prompt, "address redirect: invalid local address\n");
214       return 1;
215     }
216     error = StrToAddr(arg->argv[arg->argn+1], &aliasaddr);
217     if (error) {
218       prompt_Printf(arg->prompt, "address redirect: invalid alias address\n");
219       prompt_Printf(arg->prompt, "usage: nat %s %s\n", arg->cmd->name,
220                     arg->cmd->syntax);
221       return 1;
222     }
223     link = LibAliasRedirectAddr(la, localaddr, aliasaddr);
224     if (link == NULL) {
225       prompt_Printf(arg->prompt, "address redirect: packet aliasing"
226                     " engine error\n");
227       prompt_Printf(arg->prompt, "usage: nat %s %s\n", arg->cmd->name,
228                     arg->cmd->syntax);
229     }
230   } else
231     return -1;
232 
233   return 0;
234 }
235 
236 
237 int
238 nat_RedirectProto(struct cmdargs const *arg)
239 {
240   if (!arg->bundle->NatEnabled) {
241     prompt_Printf(arg->prompt, "nat not enabled\n");
242     return 1;
243   } else if (arg->argc >= arg->argn + 2 && arg->argc <= arg->argn + 4) {
244     struct in_addr localIP, publicIP, remoteIP;
245     struct alias_link *link;
246     struct protoent *pe;
247     int error;
248     unsigned 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 = LibAliasRedirectProto(la, 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 LibAliasProxyRule(la, 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     LibAliasSetTarget(la, 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     LibAliasSetTarget(la, 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   LibAliasSetTarget(la, 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     LibAliasSetMode(la, 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   LibAliasSetFWBase(la, base, count);
468   LibAliasSetMode(la, PKT_ALIAS_PUNCH_FW, PKT_ALIAS_PUNCH_FW);
469 
470   return 0;
471 }
472 #endif
473 
474 int
475 nat_SkinnyPort(struct cmdargs const *arg)
476 {
477   char *end;
478   long port;
479 
480   if (arg->argc == arg->argn) {
481     LibAliasSetSkinnyPort(la, 0);
482     return 0;
483   }
484 
485   if (arg->argc != arg->argn + 1)
486     return -1;
487 
488   port = strtol(arg->argv[arg->argn], &end, 10);
489   if (*end != '\0' || port < 0)
490     return -1;
491 
492   LibAliasSetSkinnyPort(la, port);
493 
494   return 0;
495 }
496 
497 static struct mbuf *
498 nat_LayerPush(struct bundle *bundle, struct link *l __unused, struct mbuf *bp,
499                 int pri __unused, u_short *proto)
500 {
501   if (!bundle->NatEnabled || *proto != PROTO_IP)
502     return bp;
503 
504   log_Printf(LogDEBUG, "nat_LayerPush: PROTO_IP -> PROTO_IP\n");
505   m_settype(bp, MB_NATOUT);
506   /* Ensure there's a bit of extra buffer for the NAT code... */
507   bp = m_pullup(m_append(bp, NULL, NAT_EXTRABUF));
508   LibAliasOut(la, MBUF_CTOP(bp), bp->m_len);
509   bp->m_len = ntohs(((struct ip *)MBUF_CTOP(bp))->ip_len);
510 
511   return bp;
512 }
513 
514 static struct mbuf *
515 nat_LayerPull(struct bundle *bundle, struct link *l __unused, struct mbuf *bp,
516                 u_short *proto)
517 {
518   static int gfrags;
519   int ret, len, nfrags;
520   struct mbuf **last;
521   char *fptr;
522 
523   if (!bundle->NatEnabled || *proto != PROTO_IP)
524     return bp;
525 
526   log_Printf(LogDEBUG, "nat_LayerPull: PROTO_IP -> PROTO_IP\n");
527   m_settype(bp, MB_NATIN);
528   /* Ensure there's a bit of extra buffer for the NAT code... */
529   bp = m_pullup(m_append(bp, NULL, NAT_EXTRABUF));
530   ret = LibAliasIn(la, MBUF_CTOP(bp), bp->m_len);
531 
532   bp->m_len = ntohs(((struct ip *)MBUF_CTOP(bp))->ip_len);
533   if (bp->m_len > MAX_MRU) {
534     log_Printf(LogWARN, "nat_LayerPull: Problem with IP header length (%lu)\n",
535                (unsigned long)bp->m_len);
536     m_freem(bp);
537     return NULL;
538   }
539 
540   switch (ret) {
541     case PKT_ALIAS_OK:
542       break;
543 
544     case PKT_ALIAS_UNRESOLVED_FRAGMENT:
545       /* Save the data for later */
546       if ((fptr = malloc(bp->m_len)) == NULL) {
547 	log_Printf(LogWARN, "nat_LayerPull: Dropped unresolved fragment -"
548 		   " out of memory!\n");
549 	m_freem(bp);
550 	bp = NULL;
551       } else {
552 	bp = mbuf_Read(bp, fptr, bp->m_len);
553 	LibAliasSaveFragment(la, fptr);
554 	log_Printf(LogDEBUG, "Store another frag (%lu) - now %d\n",
555 		   (unsigned long)((struct ip *)fptr)->ip_id, ++gfrags);
556       }
557       break;
558 
559     case PKT_ALIAS_FOUND_HEADER_FRAGMENT:
560       /* Fetch all the saved fragments and chain them on the end of `bp' */
561       last = &bp->m_nextpkt;
562       nfrags = 0;
563       while ((fptr = LibAliasGetFragment(la, MBUF_CTOP(bp))) != NULL) {
564         nfrags++;
565         LibAliasFragmentIn(la, MBUF_CTOP(bp), fptr);
566         len = ntohs(((struct ip *)fptr)->ip_len);
567         *last = m_get(len, MB_NATIN);
568         memcpy(MBUF_CTOP(*last), fptr, len);
569         free(fptr);
570         last = &(*last)->m_nextpkt;
571       }
572       gfrags -= nfrags;
573       log_Printf(LogDEBUG, "Found a frag header (%lu) - plus %d more frags (no"
574                  "w %d)\n", (unsigned long)((struct ip *)MBUF_CTOP(bp))->ip_id,
575                  nfrags, gfrags);
576       break;
577 
578     case PKT_ALIAS_IGNORED:
579       if (LibAliasSetMode(la, 0, 0) & PKT_ALIAS_DENY_INCOMING) {
580         log_Printf(LogTCPIP, "NAT engine denied data:\n");
581         m_freem(bp);
582         bp = NULL;
583       } else if (log_IsKept(LogTCPIP)) {
584         log_Printf(LogTCPIP, "NAT engine ignored data:\n");
585         PacketCheck(bundle, AF_INET, MBUF_CTOP(bp), bp->m_len, NULL,
586                     NULL, NULL);
587       }
588       break;
589 
590     default:
591       log_Printf(LogWARN, "nat_LayerPull: Dropped a packet (%d)....\n", ret);
592       m_freem(bp);
593       bp = NULL;
594       break;
595   }
596 
597   return bp;
598 }
599 
600 struct layer natlayer =
601   { LAYER_NAT, "nat", nat_LayerPush, nat_LayerPull };
602