xref: /freebsd/usr.sbin/ppp/nat_cmd.c (revision 23f282aa31e9b6fceacd449020e936e98d6f2298)
1 /*-
2  * The code in this file was written by Eivind Eklund <perhaps@yes.no>,
3  * who places it in the public domain without restriction.
4  *
5  * $FreeBSD$
6  */
7 
8 #include <sys/param.h>
9 #include <netinet/in.h>
10 #include <arpa/inet.h>
11 #include <netdb.h>
12 #include <netinet/in_systm.h>
13 #include <netinet/in.h>
14 #include <netinet/ip.h>
15 #include <sys/un.h>
16 
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <termios.h>
21 
22 #ifdef LOCALNAT
23 #include "alias.h"
24 #else
25 #include <alias.h>
26 #endif
27 
28 #include "layer.h"
29 #include "proto.h"
30 #include "defs.h"
31 #include "command.h"
32 #include "log.h"
33 #include "nat_cmd.h"
34 #include "descriptor.h"
35 #include "prompt.h"
36 #include "timer.h"
37 #include "fsm.h"
38 #include "slcompress.h"
39 #include "throughput.h"
40 #include "iplist.h"
41 #include "mbuf.h"
42 #include "lqr.h"
43 #include "hdlc.h"
44 #include "ipcp.h"
45 #include "lcp.h"
46 #include "ccp.h"
47 #include "link.h"
48 #include "mp.h"
49 #include "filter.h"
50 #ifndef NORADIUS
51 #include "radius.h"
52 #endif
53 #include "ip.h"
54 #include "bundle.h"
55 
56 
57 #define NAT_EXTRABUF (13)
58 
59 static int StrToAddr(const char *, struct in_addr *);
60 static int StrToPortRange(const char *, u_short *, u_short *, const char *);
61 static int StrToAddrAndPort(const char *, struct in_addr *, u_short *,
62                             u_short *, const char *);
63 
64 static void
65 lowhigh(u_short *a, u_short *b)
66 {
67   if (a > b) {
68     u_short c;
69 
70     c = *b;
71     *b = *a;
72     *a = c;
73   }
74 }
75 
76 int
77 nat_RedirectPort(struct cmdargs const *arg)
78 {
79   if (!arg->bundle->NatEnabled) {
80     prompt_Printf(arg->prompt, "Alias not enabled\n");
81     return 1;
82   } else if (arg->argc == arg->argn + 3 || arg->argc == arg->argn + 4) {
83     char proto_constant;
84     const char *proto;
85     struct in_addr localaddr;
86     u_short hlocalport, llocalport;
87     struct in_addr aliasaddr;
88     u_short haliasport, laliasport;
89     struct in_addr remoteaddr;
90     u_short hremoteport, lremoteport;
91     struct alias_link *link;
92     int error;
93 
94     proto = arg->argv[arg->argn];
95     if (strcmp(proto, "tcp") == 0) {
96       proto_constant = IPPROTO_TCP;
97     } else if (strcmp(proto, "udp") == 0) {
98       proto_constant = IPPROTO_UDP;
99     } else {
100       prompt_Printf(arg->prompt, "port redirect: protocol must be"
101                     " tcp or udp\n");
102       return -1;
103     }
104 
105     error = StrToAddrAndPort(arg->argv[arg->argn+1], &localaddr, &llocalport,
106                              &hlocalport, proto);
107     if (error) {
108       prompt_Printf(arg->prompt, "nat port: error reading localaddr:port\n");
109       return -1;
110     }
111 
112     error = StrToPortRange(arg->argv[arg->argn+2], &laliasport, &haliasport,
113                            proto);
114     if (error) {
115       prompt_Printf(arg->prompt, "nat port: error reading alias port\n");
116       return -1;
117     }
118     aliasaddr.s_addr = INADDR_ANY;
119 
120     if (arg->argc == arg->argn + 4) {
121       error = StrToAddrAndPort(arg->argv[arg->argn+3], &remoteaddr,
122                                &lremoteport, &hremoteport, proto);
123       if (error) {
124         prompt_Printf(arg->prompt, "nat port: error reading "
125                       "remoteaddr:port\n");
126         return -1;
127       }
128     } else {
129       remoteaddr.s_addr = INADDR_ANY;
130       lremoteport = hremoteport = 0;
131     }
132 
133     lowhigh(&llocalport, &hlocalport);
134     lowhigh(&laliasport, &haliasport);
135     lowhigh(&lremoteport, &hremoteport);
136 
137     if (haliasport - laliasport != hlocalport - llocalport) {
138       prompt_Printf(arg->prompt, "nat port: local & alias port ranges "
139                     "are not equal\n");
140       return -1;
141     }
142 
143     if (hremoteport && hremoteport - lremoteport != hlocalport - llocalport) {
144       prompt_Printf(arg->prompt, "nat port: local & remote port ranges "
145                     "are not equal\n");
146       return -1;
147     }
148 
149     while (laliasport <= haliasport) {
150       link = PacketAliasRedirectPort(localaddr, htons(llocalport),
151 				     remoteaddr, htons(lremoteport),
152                                      aliasaddr, htons(laliasport),
153 				     proto_constant);
154 
155       if (link == NULL) {
156         prompt_Printf(arg->prompt, "nat port: %d: error %d\n", laliasport,
157                       error);
158         return 1;
159       }
160       llocalport++;
161       laliasport++;
162       if (hremoteport)
163         lremoteport++;
164     }
165 
166     return 0;
167   }
168 
169   return -1;
170 }
171 
172 
173 int
174 nat_RedirectAddr(struct cmdargs const *arg)
175 {
176   if (!arg->bundle->NatEnabled) {
177     prompt_Printf(arg->prompt, "nat not enabled\n");
178     return 1;
179   } else if (arg->argc == arg->argn+2) {
180     int error;
181     struct in_addr localaddr, aliasaddr;
182     struct alias_link *link;
183 
184     error = StrToAddr(arg->argv[arg->argn], &localaddr);
185     if (error) {
186       prompt_Printf(arg->prompt, "address redirect: invalid local address\n");
187       return 1;
188     }
189     error = StrToAddr(arg->argv[arg->argn+1], &aliasaddr);
190     if (error) {
191       prompt_Printf(arg->prompt, "address redirect: invalid alias address\n");
192       prompt_Printf(arg->prompt, "Usage: nat %s %s\n", arg->cmd->name,
193                     arg->cmd->syntax);
194       return 1;
195     }
196     link = PacketAliasRedirectAddr(localaddr, aliasaddr);
197     if (link == NULL) {
198       prompt_Printf(arg->prompt, "address redirect: packet aliasing"
199                     " engine error\n");
200       prompt_Printf(arg->prompt, "Usage: nat %s %s\n", arg->cmd->name,
201                     arg->cmd->syntax);
202     }
203   } else
204     return -1;
205 
206   return 0;
207 }
208 
209 
210 static int
211 StrToAddr(const char *str, struct in_addr *addr)
212 {
213   struct hostent *hp;
214 
215   if (inet_aton(str, addr))
216     return 0;
217 
218   hp = gethostbyname(str);
219   if (!hp) {
220     log_Printf(LogWARN, "StrToAddr: Unknown host %s.\n", str);
221     return -1;
222   }
223   *addr = *((struct in_addr *) hp->h_addr);
224   return 0;
225 }
226 
227 
228 static int
229 StrToPort(const char *str, u_short *port, const char *proto)
230 {
231   struct servent *sp;
232   char *end;
233 
234   *port = strtol(str, &end, 10);
235   if (*end != '\0') {
236     sp = getservbyname(str, proto);
237     if (sp == NULL) {
238       log_Printf(LogWARN, "StrToAddr: Unknown port or service %s/%s.\n",
239 	        str, proto);
240       return -1;
241     }
242     *port = ntohs(sp->s_port);
243   }
244 
245   return 0;
246 }
247 
248 static int
249 StrToPortRange(const char *str, u_short *low, u_short *high, const char *proto)
250 {
251   char *minus;
252   int res;
253 
254   minus = strchr(str, '-');
255   if (minus)
256     *minus = '\0';		/* Cheat the const-ness ! */
257 
258   res = StrToPort(str, low, proto);
259 
260   if (minus)
261     *minus = '-';		/* Cheat the const-ness ! */
262 
263   if (res == 0) {
264     if (minus)
265       res = StrToPort(minus + 1, high, proto);
266     else
267       *high = *low;
268   }
269 
270   return res;
271 }
272 
273 static int
274 StrToAddrAndPort(const char *str, struct in_addr *addr, u_short *low,
275                  u_short *high, const char *proto)
276 {
277   char *colon;
278   int res;
279 
280   colon = strchr(str, ':');
281   if (!colon) {
282     log_Printf(LogWARN, "StrToAddrAndPort: %s is missing port number.\n", str);
283     return -1;
284   }
285 
286   *colon = '\0';		/* Cheat the const-ness ! */
287   res = StrToAddr(str, addr);
288   *colon = ':';			/* Cheat the const-ness ! */
289   if (res != 0)
290     return -1;
291 
292   return StrToPortRange(colon + 1, low, high, proto);
293 }
294 
295 int
296 nat_ProxyRule(struct cmdargs const *arg)
297 {
298   char cmd[LINE_LEN];
299   int f, pos;
300   size_t len;
301 
302   if (arg->argn >= arg->argc)
303     return -1;
304 
305   for (f = arg->argn, pos = 0; f < arg->argc; f++) {
306     len = strlen(arg->argv[f]);
307     if (sizeof cmd - pos < len + (f ? 1 : 0))
308       break;
309     if (f)
310       cmd[pos++] = ' ';
311     strcpy(cmd + pos, arg->argv[f]);
312     pos += len;
313   }
314 
315   return PacketAliasProxyRule(cmd);
316 }
317 
318 int
319 nat_Pptp(struct cmdargs const *arg)
320 {
321   struct in_addr addr;
322 
323   if (arg->argc == arg->argn) {
324     addr.s_addr = INADDR_NONE;
325     PacketAliasPptp(addr);
326     return 0;
327   }
328 
329   if (arg->argc != arg->argn + 1)
330     return -1;
331 
332   addr = GetIpAddr(arg->argv[arg->argn]);
333   if (addr.s_addr == INADDR_NONE) {
334     log_Printf(LogWARN, "%s: invalid address\n", arg->argv[arg->argn]);
335     return 1;
336   }
337 
338   PacketAliasPptp(addr);
339   return 0;
340 }
341 
342 int
343 nat_SetTarget(struct cmdargs const *arg)
344 {
345   struct in_addr addr;
346 
347   if (arg->argc == arg->argn) {
348     addr.s_addr = INADDR_NONE;
349     PacketAliasSetTarget(addr);
350     return 0;
351   }
352 
353   if (arg->argc != arg->argn + 1)
354     return -1;
355 
356   addr = GetIpAddr(arg->argv[arg->argn]);
357   if (addr.s_addr == INADDR_NONE) {
358     log_Printf(LogWARN, "%s: invalid address\n", arg->argv[arg->argn]);
359     return 1;
360   }
361 
362   PacketAliasSetTarget(addr);
363   return 0;
364 }
365 
366 static struct mbuf *
367 nat_LayerPush(struct bundle *bundle, struct link *l, struct mbuf *bp,
368                 int pri, u_short *proto)
369 {
370   if (!bundle->NatEnabled || *proto != PROTO_IP)
371     return bp;
372 
373   log_Printf(LogDEBUG, "nat_LayerPush: PROTO_IP -> PROTO_IP\n");
374   m_settype(bp, MB_NATOUT);
375   /* Ensure there's a bit of extra buffer for the NAT code... */
376   bp = m_pullup(m_append(bp, NULL, NAT_EXTRABUF));
377   PacketAliasOut(MBUF_CTOP(bp), bp->m_len);
378   bp->m_len = ntohs(((struct ip *)MBUF_CTOP(bp))->ip_len);
379 
380   return bp;
381 }
382 
383 static struct mbuf *
384 nat_LayerPull(struct bundle *bundle, struct link *l, struct mbuf *bp,
385                 u_short *proto)
386 {
387   static int gfrags;
388   struct ip *pip, *piip;
389   int ret, len, nfrags;
390   struct mbuf **last;
391   char *fptr;
392 
393   if (!bundle->NatEnabled || *proto != PROTO_IP)
394     return bp;
395 
396   log_Printf(LogDEBUG, "nat_LayerPull: PROTO_IP -> PROTO_IP\n");
397   m_settype(bp, MB_NATIN);
398   bp = m_pullup(bp);
399   pip = (struct ip *)MBUF_CTOP(bp);
400   piip = (struct ip *)((char *)pip + (pip->ip_hl << 2));
401 
402   if (pip->ip_p == IPPROTO_IGMP ||
403       (pip->ip_p == IPPROTO_IPIP && IN_CLASSD(ntohl(piip->ip_dst.s_addr))))
404     return bp;
405 
406   /* Ensure there's a bit of extra buffer for the NAT code... */
407   bp = m_pullup(m_append(bp, NULL, NAT_EXTRABUF));
408   ret = PacketAliasIn(MBUF_CTOP(bp), bp->m_len);
409   pip = (struct ip *)MBUF_CTOP(bp);
410 
411   bp->m_len = ntohs(pip->ip_len);
412   if (bp->m_len > MAX_MRU) {
413     log_Printf(LogWARN, "nat_LayerPull: Problem with IP header length (%lu)\n",
414                (unsigned long)bp->m_len);
415     m_freem(bp);
416     return NULL;
417   }
418 
419   switch (ret) {
420     case PKT_ALIAS_OK:
421       break;
422 
423     case PKT_ALIAS_UNRESOLVED_FRAGMENT:
424       /* Save the data for later */
425       fptr = malloc(bp->m_len);
426       bp = mbuf_Read(bp, fptr, bp->m_len);
427       PacketAliasSaveFragment(fptr);
428       log_Printf(LogDEBUG, "Store another frag (%lu) - now %d\n",
429                  (unsigned long)((struct ip *)fptr)->ip_id, ++gfrags);
430       break;
431 
432     case PKT_ALIAS_FOUND_HEADER_FRAGMENT:
433       /* Fetch all the saved fragments and chain them on the end of `bp' */
434       last = &bp->m_nextpkt;
435       nfrags = 0;
436       while ((fptr = PacketAliasGetFragment(MBUF_CTOP(bp))) != NULL) {
437         nfrags++;
438         PacketAliasFragmentIn(MBUF_CTOP(bp), fptr);
439         len = ntohs(((struct ip *)fptr)->ip_len);
440         *last = m_get(len, MB_NATIN);
441         memcpy(MBUF_CTOP(*last), fptr, len);
442         free(fptr);
443         last = &(*last)->m_nextpkt;
444       }
445       gfrags -= nfrags;
446       log_Printf(LogDEBUG, "Found a frag header (%lu) - plus %d more frags (no"
447                  "w %d)\n", (unsigned long)((struct ip *)MBUF_CTOP(bp))->ip_id,
448                  nfrags, gfrags);
449       break;
450 
451     case PKT_ALIAS_IGNORED:
452       if (log_IsKept(LogTCPIP)) {
453         log_Printf(LogTCPIP, "NAT engine ignored data:\n");
454         PacketCheck(bundle, (char *)pip, ntohs(pip->ip_len), NULL);
455       }
456       m_freem(bp);
457       bp = NULL;
458       break;
459 
460     default:
461       log_Printf(LogWARN, "nat_LayerPull: Dropped a packet (%d)....\n", ret);
462       m_freem(bp);
463       bp = NULL;
464       break;
465   }
466 
467   return bp;
468 }
469 
470 struct layer natlayer =
471   { LAYER_NAT, "nat", nat_LayerPush, nat_LayerPull };
472