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