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