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