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