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
87static int StrToAddr(const char *, struct in_addr *);
88static int StrToPortRange(const char *, u_short *, u_short *, const char *);
89static int StrToAddrAndPort(const char *, struct in_addr *, u_short *,
90                            u_short *, const char *);
91
92extern struct libalias *la;
93
94static void
95lowhigh(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
106int
107nat_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
202int
203nat_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
239int
240nat_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
308static int
309StrToAddr(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
326static int
327StrToPort(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
346static int
347StrToPortRange(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
371static int
372StrToAddrAndPort(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
393int
394nat_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
416int
417nat_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
447int
448nat_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
476int
477nat_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
499static struct mbuf *
500nat_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
516static struct mbuf *
517nat_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
602struct layer natlayer =
603  { LAYER_NAT, "nat", nat_LayerPush, nat_LayerPull };
604