Refactored icmpkick6
[akaros.git] / kern / src / net / gre.c
1 #include "u.h"
2 #include "../port/lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 #include "../port/error.h"
7
8 #include "ip.h"
9
10 #define DPRINT if(0)print
11
12 enum {
13         GRE_IPONLY = 12,                        /* size of ip header */
14         GRE_IPPLUSGRE = 12,     /* minimum size of GRE header */
15         IP_GREPROTO = 47,
16
17         GRErxms = 200,
18         GREtickms = 100,
19         GREmaxxmit = 10,
20 };
21
22 typedef struct GREhdr {
23         /* ip header */
24         uchar vihl;                                     /* Version and header length */
25         uchar tos;                                      /* Type of service */
26         uchar len[2];                           /* packet length (including headers) */
27         uchar id[2];                            /* Identification */
28         uchar frag[2];                          /* Fragment information */
29         uchar Unused;
30         uchar proto;                            /* Protocol */
31         uchar cksum[2];                         /* checksum */
32         uchar src[4];                           /* Ip source */
33         uchar dst[4];                           /* Ip destination */
34
35         /* gre header */
36         uchar flags[2];
37         uchar eproto[2];                        /* encapsulation protocol */
38 } GREhdr;
39
40 typedef struct GREpriv GREpriv;
41 struct GREpriv {
42         int raw;                                        /* Raw GRE mode */
43
44         /* non-MIB stats */
45         ulong csumerr;                          /* checksum errors */
46         ulong lenerr;                           /* short packet */
47 };
48
49 static void grekick(void *x, Block * bp);
50
51 static char *greconnect(Conv * c, char **argv, int argc)
52 {
53         Proto *p;
54         char *err;
55         Conv *tc, **cp, **ecp;
56
57         err = Fsstdconnect(c, argv, argc);
58         if (err != nil)
59                 return err;
60
61         /* make sure noone's already connected to this other sys */
62         p = c->p;
63         qlock(p);
64         ecp = &p->conv[p->nc];
65         for (cp = p->conv; cp < ecp; cp++) {
66                 tc = *cp;
67                 if (tc == nil)
68                         break;
69                 if (tc == c)
70                         continue;
71                 if (tc->rport == c->rport && ipcmp(tc->raddr, c->raddr) == 0) {
72                         err = "already connected to that addr/proto";
73                         ipmove(c->laddr, IPnoaddr);
74                         ipmove(c->raddr, IPnoaddr);
75                         break;
76                 }
77         }
78         qunlock(p);
79
80         if (err != nil)
81                 return err;
82         Fsconnected(c, nil);
83
84         return nil;
85 }
86
87 static void grecreate(Conv * c)
88 {
89         c->rq = qopen(64 * 1024, Qmsg, 0, c);
90         c->wq = qbypass(grekick, c);
91 }
92
93 static int grestate(Conv * c, char *state, int n)
94 {
95         USED(c);
96         return snprint(state, n, "%s", "Datagram");
97 }
98
99 static char *greannounce(Conv *, char **, int)
100 {
101         return "pktifc does not support announce";
102 }
103
104 static void greclose(Conv * c)
105 {
106         qclose(c->rq);
107         qclose(c->wq);
108         qclose(c->eq);
109         ipmove(c->laddr, IPnoaddr);
110         ipmove(c->raddr, IPnoaddr);
111         c->lport = 0;
112         c->rport = 0;
113 }
114
115 int drop;
116
117 static void grekick(void *x, Block * bp)
118 {
119         Conv *c = x;
120         GREhdr *ghp;
121         uchar laddr[IPaddrlen], raddr[IPaddrlen];
122
123         if (bp == nil)
124                 return;
125
126         /* Make space to fit ip header (gre header already there) */
127         bp = padblock(bp, GRE_IPONLY);
128         if (bp == nil)
129                 return;
130
131         /* make sure the message has a GRE header */
132         bp = pullupblock(bp, GRE_IPONLY + GRE_IPPLUSGRE);
133         if (bp == nil)
134                 return;
135
136         ghp = (GREhdr *) (bp->rp);
137         ghp->vihl = IP_VER4;
138
139         if (!((GREpriv *) c->p->priv)->raw) {
140                 v4tov6(raddr, ghp->dst);
141                 if (ipcmp(raddr, v4prefix) == 0)
142                         memmove(ghp->dst, c->raddr + IPv4off, IPv4addrlen);
143                 v4tov6(laddr, ghp->src);
144                 if (ipcmp(laddr, v4prefix) == 0) {
145                         if (ipcmp(c->laddr, IPnoaddr) == 0)
146                                 findlocalip(c->p->f, c->laddr, raddr);  /* pick interface closest to dest */
147                         memmove(ghp->src, c->laddr + IPv4off, IPv4addrlen);
148                 }
149                 hnputs(ghp->eproto, c->rport);
150         }
151
152         ghp->proto = IP_GREPROTO;
153         ghp->frag[0] = 0;
154         ghp->frag[1] = 0;
155
156         ipoput4(c->p->f, bp, 0, c->ttl, c->tos, nil);
157 }
158
159 static void greiput(Proto * gre, Ipifc *, Block * bp)
160 {
161         int len;
162         GREhdr *ghp;
163         Conv *c, **p;
164         ushort eproto;
165         uchar raddr[IPaddrlen];
166         GREpriv *gpriv;
167
168         gpriv = gre->priv;
169         ghp = (GREhdr *) (bp->rp);
170
171         v4tov6(raddr, ghp->src);
172         eproto = nhgets(ghp->eproto);
173         qlock(gre);
174
175         /* Look for a conversation structure for this port and address */
176         c = nil;
177         for (p = gre->conv; *p; p++) {
178                 c = *p;
179                 if (c->inuse == 0)
180                         continue;
181                 if (c->rport == eproto && (gpriv->raw || ipcmp(c->raddr, raddr) == 0))
182                         break;
183         }
184
185         if (*p == nil) {
186                 qunlock(gre);
187                 freeblist(bp);
188                 return;
189         }
190
191         qunlock(gre);
192
193         /*
194          * Trim the packet down to data size
195          */
196         len = nhgets(ghp->len) - GRE_IPONLY;
197         if (len < GRE_IPPLUSGRE) {
198                 freeblist(bp);
199                 return;
200         }
201         bp = trimblock(bp, GRE_IPONLY, len);
202         if (bp == nil) {
203                 gpriv->lenerr++;
204                 return;
205         }
206
207         /*
208          *  Can't delimit packet so pull it all into one block.
209          */
210         if (qlen(c->rq) > 64 * 1024)
211                 freeblist(bp);
212         else {
213                 bp = concatblock(bp);
214                 if (bp == 0)
215                         panic("greiput");
216                 qpass(c->rq, bp);
217         }
218 }
219
220 int grestats(Proto * gre, char *buf, int len)
221 {
222         GREpriv *gpriv;
223
224         gpriv = gre->priv;
225
226         return snprint(buf, len, "gre: len %lu\n", gpriv->lenerr);
227 }
228
229 char *grectl(Conv * c, char **f, int n)
230 {
231         GREpriv *gpriv;
232
233         gpriv = c->p->priv;
234         if (n == 1) {
235                 if (strcmp(f[0], "raw") == 0) {
236                         gpriv->raw = 1;
237                         return nil;
238                 } else if (strcmp(f[0], "cooked") == 0) {
239                         gpriv->raw = 0;
240                         return nil;
241                 }
242         }
243         return "unknown control request";
244 }
245
246 void greinit(Fs * fs)
247 {
248         Proto *gre;
249
250         gre = smalloc(sizeof(Proto));
251         gre->priv = smalloc(sizeof(GREpriv));
252         gre->name = "gre";
253         gre->connect = greconnect;
254         gre->announce = greannounce;
255         gre->state = grestate;
256         gre->create = grecreate;
257         gre->close = greclose;
258         gre->rcv = greiput;
259         gre->ctl = grectl;
260         gre->advise = nil;
261         gre->stats = grestats;
262         gre->ipproto = IP_GREPROTO;
263         gre->nc = 64;
264         gre->ptclsize = 0;
265
266         Fsproto(fs, gre);
267 }