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