nat_cmd.c revision 77690
1285SN/A/*-
2462SN/A * Copyright (c) 2001 Charles Mott <cmott@scientech.com>
3285SN/A *                    Brian Somers <brian@Awfulhak.org>
4285SN/A * All rights reserved.
5285SN/A *
6285SN/A * Redistribution and use in source and binary forms, with or without
7285SN/A * modification, are permitted provided that the following conditions
8285SN/A * are met:
9285SN/A * 1. Redistributions of source code must retain the above copyright
10285SN/A *    notice, this list of conditions and the following disclaimer.
11285SN/A * 2. Redistributions in binary form must reproduce the above copyright
12285SN/A *    notice, this list of conditions and the following disclaimer in the
13285SN/A *    documentation and/or other materials provided with the distribution.
14285SN/A *
15285SN/A * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16285SN/A * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17285SN/A * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18285SN/A * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19285SN/A * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20285SN/A * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21285SN/A * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22285SN/A * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23285SN/A * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24285SN/A * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25285SN/A * SUCH DAMAGE.
26285SN/A *
27285SN/A * $FreeBSD: head/usr.sbin/ppp/nat_cmd.c 77690 2001-06-04 14:38:29Z brian $
28285SN/A */
29285SN/A
30285SN/A#include <sys/param.h>
31285SN/A#include <netinet/in.h>
32285SN/A#include <arpa/inet.h>
33285SN/A#include <netdb.h>
34285SN/A#include <netinet/in_systm.h>
35285SN/A#include <netinet/in.h>
36285SN/A#include <netinet/ip.h>
37285SN/A#include <sys/un.h>
38285SN/A
39285SN/A#include <stdio.h>
40285SN/A#include <stdlib.h>
41285SN/A#include <string.h>
42285SN/A#include <termios.h>
43285SN/A
44285SN/A#ifdef LOCALNAT
45285SN/A#include "alias.h"
46285SN/A#else
47285SN/A#include <alias.h>
48285SN/A#endif
49285SN/A
50285SN/A#include "layer.h"
51285SN/A#include "proto.h"
52285SN/A#include "defs.h"
53285SN/A#include "command.h"
54285SN/A#include "log.h"
55285SN/A#include "nat_cmd.h"
56285SN/A#include "descriptor.h"
57285SN/A#include "prompt.h"
58285SN/A#include "timer.h"
59285SN/A#include "fsm.h"
60285SN/A#include "slcompress.h"
61285SN/A#include "throughput.h"
62285SN/A#include "iplist.h"
63285SN/A#include "mbuf.h"
64285SN/A#include "lqr.h"
65285SN/A#include "hdlc.h"
66285SN/A#include "ipcp.h"
67285SN/A#include "lcp.h"
68285SN/A#include "ccp.h"
69285SN/A#include "link.h"
70285SN/A#include "mp.h"
71285SN/A#include "filter.h"
72285SN/A#ifndef NORADIUS
73285SN/A#include "radius.h"
74285SN/A#endif
75285SN/A#include "ip.h"
76285SN/A#include "bundle.h"
77285SN/A
78285SN/A
79285SN/A#define NAT_EXTRABUF (13)
80285SN/A
81285SN/Astatic int StrToAddr(const char *, struct in_addr *);
82285SN/Astatic int StrToPortRange(const char *, u_short *, u_short *, const char *);
83285SN/Astatic int StrToAddrAndPort(const char *, struct in_addr *, u_short *,
84285SN/A                            u_short *, const char *);
85285SN/A
86285SN/Astatic void
87285SN/Alowhigh(u_short *a, u_short *b)
88285SN/A{
89285SN/A  if (a > b) {
90285SN/A    u_short c;
91367SN/A
92285SN/A    c = *b;
93285SN/A    *b = *a;
94285SN/A    *a = c;
95285SN/A  }
96285SN/A}
97285SN/A
98285SN/Aint
99285SN/Anat_RedirectPort(struct cmdargs const *arg)
100285SN/A{
101285SN/A  if (!arg->bundle->NatEnabled) {
102285SN/A    prompt_Printf(arg->prompt, "Alias not enabled\n");
103285SN/A    return 1;
104285SN/A  } else if (arg->argc == arg->argn + 3 || arg->argc == arg->argn + 4) {
105285SN/A    char proto_constant;
106285SN/A    const char *proto;
107285SN/A    struct in_addr localaddr;
108285SN/A    u_short hlocalport, llocalport;
109285SN/A    struct in_addr aliasaddr;
110285SN/A    u_short haliasport, laliasport;
111285SN/A    struct in_addr remoteaddr;
112285SN/A    u_short hremoteport, lremoteport;
113285SN/A    struct alias_link *link;
114285SN/A    int error;
115285SN/A
116285SN/A    proto = arg->argv[arg->argn];
117285SN/A    if (strcmp(proto, "tcp") == 0) {
118285SN/A      proto_constant = IPPROTO_TCP;
119285SN/A    } else if (strcmp(proto, "udp") == 0) {
120285SN/A      proto_constant = IPPROTO_UDP;
121285SN/A    } else {
122285SN/A      prompt_Printf(arg->prompt, "port redirect: protocol must be"
123285SN/A                    " tcp or udp\n");
124285SN/A      return -1;
125285SN/A    }
126285SN/A
127285SN/A    error = StrToAddrAndPort(arg->argv[arg->argn+1], &localaddr, &llocalport,
128285SN/A                             &hlocalport, proto);
129285SN/A    if (error) {
130285SN/A      prompt_Printf(arg->prompt, "nat port: error reading localaddr:port\n");
131285SN/A      return -1;
132285SN/A    }
133285SN/A
134285SN/A    error = StrToPortRange(arg->argv[arg->argn+2], &laliasport, &haliasport,
135285SN/A                           proto);
136285SN/A    if (error) {
137285SN/A      prompt_Printf(arg->prompt, "nat port: error reading alias port\n");
138285SN/A      return -1;
139285SN/A    }
140285SN/A    aliasaddr.s_addr = INADDR_ANY;
141285SN/A
142285SN/A    if (arg->argc == arg->argn + 4) {
143285SN/A      error = StrToAddrAndPort(arg->argv[arg->argn+3], &remoteaddr,
144285SN/A                               &lremoteport, &hremoteport, proto);
145285SN/A      if (error) {
146285SN/A        prompt_Printf(arg->prompt, "nat port: error reading "
147285SN/A                      "remoteaddr:port\n");
148285SN/A        return -1;
149285SN/A      }
150285SN/A    } else {
151285SN/A      remoteaddr.s_addr = INADDR_ANY;
152285SN/A      lremoteport = hremoteport = 0;
153285SN/A    }
154285SN/A
155285SN/A    lowhigh(&llocalport, &hlocalport);
156285SN/A    lowhigh(&laliasport, &haliasport);
157285SN/A    lowhigh(&lremoteport, &hremoteport);
158285SN/A
159285SN/A    if (haliasport - laliasport != hlocalport - llocalport) {
160285SN/A      prompt_Printf(arg->prompt, "nat port: local & alias port ranges "
161285SN/A                    "are not equal\n");
162285SN/A      return -1;
163285SN/A    }
164285SN/A
165285SN/A    if (hremoteport && hremoteport - lremoteport != hlocalport - llocalport) {
166285SN/A      prompt_Printf(arg->prompt, "nat port: local & remote port ranges "
167285SN/A                    "are not equal\n");
168285SN/A      return -1;
169285SN/A    }
170285SN/A
171285SN/A    while (laliasport <= haliasport) {
172285SN/A      link = PacketAliasRedirectPort(localaddr, htons(llocalport),
173285SN/A				     remoteaddr, htons(lremoteport),
174285SN/A                                     aliasaddr, htons(laliasport),
175285SN/A				     proto_constant);
176285SN/A
177285SN/A      if (link == NULL) {
178285SN/A        prompt_Printf(arg->prompt, "nat port: %d: error %d\n", laliasport,
179285SN/A                      error);
180285SN/A        return 1;
181285SN/A      }
182285SN/A      llocalport++;
183285SN/A      laliasport++;
184285SN/A      if (hremoteport)
185285SN/A        lremoteport++;
186285SN/A    }
187285SN/A
188285SN/A    return 0;
189285SN/A  }
190285SN/A
191285SN/A  return -1;
192285SN/A}
193285SN/A
194285SN/A
195285SN/Aint
196285SN/Anat_RedirectAddr(struct cmdargs const *arg)
197285SN/A{
198285SN/A  if (!arg->bundle->NatEnabled) {
199285SN/A    prompt_Printf(arg->prompt, "nat not enabled\n");
200285SN/A    return 1;
201285SN/A  } else if (arg->argc == arg->argn+2) {
202285SN/A    int error;
203285SN/A    struct in_addr localaddr, aliasaddr;
204285SN/A    struct alias_link *link;
205285SN/A
206285SN/A    error = StrToAddr(arg->argv[arg->argn], &localaddr);
207285SN/A    if (error) {
208285SN/A      prompt_Printf(arg->prompt, "address redirect: invalid local address\n");
209285SN/A      return 1;
210285SN/A    }
211285SN/A    error = StrToAddr(arg->argv[arg->argn+1], &aliasaddr);
212285SN/A    if (error) {
213285SN/A      prompt_Printf(arg->prompt, "address redirect: invalid alias address\n");
214285SN/A      prompt_Printf(arg->prompt, "Usage: nat %s %s\n", arg->cmd->name,
215285SN/A                    arg->cmd->syntax);
216285SN/A      return 1;
217285SN/A    }
218285SN/A    link = PacketAliasRedirectAddr(localaddr, aliasaddr);
219285SN/A    if (link == NULL) {
220285SN/A      prompt_Printf(arg->prompt, "address redirect: packet aliasing"
221285SN/A                    " engine error\n");
222285SN/A      prompt_Printf(arg->prompt, "Usage: nat %s %s\n", arg->cmd->name,
223285SN/A                    arg->cmd->syntax);
224285SN/A    }
225285SN/A  } else
226285SN/A    return -1;
227285SN/A
228285SN/A  return 0;
229285SN/A}
230285SN/A
231285SN/A
232285SN/Astatic int
233285SN/AStrToAddr(const char *str, struct in_addr *addr)
234285SN/A{
235285SN/A  struct hostent *hp;
236285SN/A
237285SN/A  if (inet_aton(str, addr))
238285SN/A    return 0;
239285SN/A
240285SN/A  hp = gethostbyname(str);
241285SN/A  if (!hp) {
242285SN/A    log_Printf(LogWARN, "StrToAddr: Unknown host %s.\n", str);
243285SN/A    return -1;
244285SN/A  }
245285SN/A  *addr = *((struct in_addr *) hp->h_addr);
246285SN/A  return 0;
247285SN/A}
248285SN/A
249285SN/A
250285SN/Astatic int
251285SN/AStrToPort(const char *str, u_short *port, const char *proto)
252285SN/A{
253285SN/A  struct servent *sp;
254285SN/A  char *end;
255285SN/A
256285SN/A  *port = strtol(str, &end, 10);
257285SN/A  if (*end != '\0') {
258285SN/A    sp = getservbyname(str, proto);
259285SN/A    if (sp == NULL) {
260285SN/A      log_Printf(LogWARN, "StrToAddr: Unknown port or service %s/%s.\n",
261285SN/A	        str, proto);
262285SN/A      return -1;
263285SN/A    }
264285SN/A    *port = ntohs(sp->s_port);
265285SN/A  }
266285SN/A
267285SN/A  return 0;
268285SN/A}
269285SN/A
270285SN/Astatic int
271285SN/AStrToPortRange(const char *str, u_short *low, u_short *high, const char *proto)
272285SN/A{
273285SN/A  char *minus;
274285SN/A  int res;
275285SN/A
276285SN/A  minus = strchr(str, '-');
277285SN/A  if (minus)
278285SN/A    *minus = '\0';		/* Cheat the const-ness ! */
279285SN/A
280285SN/A  res = StrToPort(str, low, proto);
281285SN/A
282285SN/A  if (minus)
283285SN/A    *minus = '-';		/* Cheat the const-ness ! */
284285SN/A
285285SN/A  if (res == 0) {
286285SN/A    if (minus)
287285SN/A      res = StrToPort(minus + 1, high, proto);
288285SN/A    else
289285SN/A      *high = *low;
290285SN/A  }
291285SN/A
292285SN/A  return res;
293285SN/A}
294285SN/A
295285SN/Astatic int
296285SN/AStrToAddrAndPort(const char *str, struct in_addr *addr, u_short *low,
297285SN/A                 u_short *high, const char *proto)
298285SN/A{
299285SN/A  char *colon;
300285SN/A  int res;
301285SN/A
302285SN/A  colon = strchr(str, ':');
303285SN/A  if (!colon) {
304285SN/A    log_Printf(LogWARN, "StrToAddrAndPort: %s is missing port number.\n", str);
305285SN/A    return -1;
306285SN/A  }
307285SN/A
308285SN/A  *colon = '\0';		/* Cheat the const-ness ! */
309285SN/A  res = StrToAddr(str, addr);
310285SN/A  *colon = ':';			/* Cheat the const-ness ! */
311285SN/A  if (res != 0)
312285SN/A    return -1;
313285SN/A
314285SN/A  return StrToPortRange(colon + 1, low, high, proto);
315285SN/A}
316285SN/A
317285SN/Aint
318285SN/Anat_ProxyRule(struct cmdargs const *arg)
319285SN/A{
320285SN/A  char cmd[LINE_LEN];
321285SN/A  int f, pos;
322285SN/A  size_t len;
323285SN/A
324285SN/A  if (arg->argn >= arg->argc)
325285SN/A    return -1;
326285SN/A
327285SN/A  for (f = arg->argn, pos = 0; f < arg->argc; f++) {
328285SN/A    len = strlen(arg->argv[f]);
329285SN/A    if (sizeof cmd - pos < len + (len ? 1 : 0))
330285SN/A      break;
331285SN/A    if (len)
332285SN/A      cmd[pos++] = ' ';
333285SN/A    strcpy(cmd + pos, arg->argv[f]);
334285SN/A    pos += len;
335285SN/A  }
336285SN/A
337285SN/A  return PacketAliasProxyRule(cmd);
338285SN/A}
339285SN/A
340285SN/Aint
341285SN/Anat_SetTarget(struct cmdargs const *arg)
342285SN/A{
343285SN/A  struct in_addr addr;
344285SN/A
345285SN/A  if (arg->argc == arg->argn) {
346285SN/A    addr.s_addr = INADDR_ANY;
347285SN/A    PacketAliasSetTarget(addr);
348285SN/A    return 0;
349285SN/A  }
350285SN/A
351285SN/A  if (arg->argc != arg->argn + 1)
352285SN/A    return -1;
353285SN/A
354285SN/A  if (!strcasecmp(arg->argv[arg->argn], "MYADDR")) {
355285SN/A    addr.s_addr = INADDR_ANY;
356285SN/A    PacketAliasSetTarget(addr);
357285SN/A    return 0;
358285SN/A  }
359285SN/A
360285SN/A  addr = GetIpAddr(arg->argv[arg->argn]);
361285SN/A  if (addr.s_addr == INADDR_NONE) {
362285SN/A    log_Printf(LogWARN, "%s: invalid address\n", arg->argv[arg->argn]);
363285SN/A    return 1;
364285SN/A  }
365285SN/A
366285SN/A  PacketAliasSetTarget(addr);
367285SN/A  return 0;
368285SN/A}
369285SN/A
370285SN/Astatic struct mbuf *
371285SN/Anat_LayerPush(struct bundle *bundle, struct link *l, struct mbuf *bp,
372285SN/A                int pri, u_short *proto)
373285SN/A{
374285SN/A  if (!bundle->NatEnabled || *proto != PROTO_IP)
375285SN/A    return bp;
376285SN/A
377285SN/A  log_Printf(LogDEBUG, "nat_LayerPush: PROTO_IP -> PROTO_IP\n");
378285SN/A  m_settype(bp, MB_NATOUT);
379285SN/A  /* Ensure there's a bit of extra buffer for the NAT code... */
380285SN/A  bp = m_pullup(m_append(bp, NULL, NAT_EXTRABUF));
381285SN/A  PacketAliasOut(MBUF_CTOP(bp), bp->m_len);
382285SN/A  bp->m_len = ntohs(((struct ip *)MBUF_CTOP(bp))->ip_len);
383285SN/A
384285SN/A  return bp;
385285SN/A}
386285SN/A
387285SN/Astatic struct mbuf *
388285SN/Anat_LayerPull(struct bundle *bundle, struct link *l, struct mbuf *bp,
389285SN/A                u_short *proto)
390285SN/A{
391285SN/A  static int gfrags;
392285SN/A  int ret, len, nfrags;
393285SN/A  struct mbuf **last;
394285SN/A  char *fptr;
395285SN/A
396285SN/A  if (!bundle->NatEnabled || *proto != PROTO_IP)
397285SN/A    return bp;
398285SN/A
399285SN/A  log_Printf(LogDEBUG, "nat_LayerPull: PROTO_IP -> PROTO_IP\n");
400285SN/A  m_settype(bp, MB_NATIN);
401285SN/A  /* Ensure there's a bit of extra buffer for the NAT code... */
402285SN/A  bp = m_pullup(m_append(bp, NULL, NAT_EXTRABUF));
403285SN/A  ret = PacketAliasIn(MBUF_CTOP(bp), bp->m_len);
404285SN/A
405285SN/A  bp->m_len = ntohs(((struct ip *)MBUF_CTOP(bp))->ip_len);
406285SN/A  if (bp->m_len > MAX_MRU) {
407285SN/A    log_Printf(LogWARN, "nat_LayerPull: Problem with IP header length (%lu)\n",
408285SN/A               (unsigned long)bp->m_len);
409285SN/A    m_freem(bp);
410285SN/A    return NULL;
411285SN/A  }
412285SN/A
413285SN/A  switch (ret) {
414285SN/A    case PKT_ALIAS_OK:
415285SN/A      break;
416285SN/A
417285SN/A    case PKT_ALIAS_UNRESOLVED_FRAGMENT:
418285SN/A      /* Save the data for later */
419285SN/A      fptr = malloc(bp->m_len);
420285SN/A      bp = mbuf_Read(bp, fptr, bp->m_len);
421285SN/A      PacketAliasSaveFragment(fptr);
422285SN/A      log_Printf(LogDEBUG, "Store another frag (%lu) - now %d\n",
423285SN/A                 (unsigned long)((struct ip *)fptr)->ip_id, ++gfrags);
424285SN/A      break;
425285SN/A
426285SN/A    case PKT_ALIAS_FOUND_HEADER_FRAGMENT:
427285SN/A      /* Fetch all the saved fragments and chain them on the end of `bp' */
428285SN/A      last = &bp->m_nextpkt;
429285SN/A      nfrags = 0;
430285SN/A      while ((fptr = PacketAliasGetFragment(MBUF_CTOP(bp))) != NULL) {
431285SN/A        nfrags++;
432285SN/A        PacketAliasFragmentIn(MBUF_CTOP(bp), fptr);
433285SN/A        len = ntohs(((struct ip *)fptr)->ip_len);
434285SN/A        *last = m_get(len, MB_NATIN);
435285SN/A        memcpy(MBUF_CTOP(*last), fptr, len);
436285SN/A        free(fptr);
437285SN/A        last = &(*last)->m_nextpkt;
438285SN/A      }
439285SN/A      gfrags -= nfrags;
440367SN/A      log_Printf(LogDEBUG, "Found a frag header (%lu) - plus %d more frags (no"
441285SN/A                 "w %d)\n", (unsigned long)((struct ip *)MBUF_CTOP(bp))->ip_id,
442285SN/A                 nfrags, gfrags);
443285SN/A      break;
444285SN/A
445285SN/A    case PKT_ALIAS_IGNORED:
446285SN/A      if (PacketAliasSetMode(0, 0) & PKT_ALIAS_DENY_INCOMING) {
447285SN/A        log_Printf(LogTCPIP, "NAT engine denied data:\n");
448285SN/A        m_freem(bp);
449285SN/A        bp = NULL;
450285SN/A      } else if (log_IsKept(LogTCPIP)) {
451285SN/A        log_Printf(LogTCPIP, "NAT engine ignored data:\n");
452285SN/A        PacketCheck(bundle, MBUF_CTOP(bp), bp->m_len, NULL, NULL, NULL);
453285SN/A      }
454285SN/A      break;
455285SN/A
456285SN/A    default:
457285SN/A      log_Printf(LogWARN, "nat_LayerPull: Dropped a packet (%d)....\n", ret);
458285SN/A      m_freem(bp);
459285SN/A      bp = NULL;
460285SN/A      break;
461285SN/A  }
462285SN/A
463285SN/A  return bp;
464285SN/A}
465285SN/A
466285SN/Astruct layer natlayer =
467285SN/A  { LAYER_NAT, "nat", nat_LayerPush, nat_LayerPull };
468285SN/A