pppoe plugin stuff - Michal Ostrowski's version, lightly hacked
[ppp.git] / pppd / plugins / pppoe / pppoe_relay.c
1 /* PPPoE support library "libpppoe"
2  *
3  * Copyright 2000 Michal Ostrowski <mostrows@styx.uwaterloo.ca>,
4  *                Jamal Hadi Salim <hadi@cyberus.ca>
5  *
6  *  This program is free software; you can redistribute it and/or
7  *  modify it under the terms of the GNU General Public License
8  *  as published by the Free Software Foundation; either version
9  *  2 of the License, or (at your option) any later version.
10  */
11
12 #include "pppoe.h"
13
14 static int relay_init_disc(struct session* ses,
15                            struct pppoe_packet *p_in,
16                            struct pppoe_packet **p_out){
17
18     ses->state = 0;
19     ses->retransmits = -1 ;
20     ses->retries = -1;
21
22     (*p_out) = NULL;
23     return 0;
24 }
25
26 static int pcid=0;
27 static int relay_rcv_padi(struct session* ses,
28                           struct pppoe_packet *p_in,
29                           struct pppoe_packet **p_out){
30     char tag_buf[32];
31     struct pppoe_con *newpc = NULL;
32     struct pppoe_tag *tag = (struct pppoe_tag *) tag_buf;
33
34
35     tag->tag_type = PTT_RELAY_SID;
36     tag->tag_len  = htons(ETH_ALEN + sizeof(struct session *));
37
38     memcpy(tag->tag_data, p_in->addr.sll_addr, ETH_ALEN);
39     memcpy(tag->tag_data + ETH_ALEN, &ses, sizeof(struct session *));
40
41     if(! p_in->tags[TAG_RELAY_SID] ){
42         copy_tag(p_in, tag);
43     }
44
45
46     poe_dbglog(ses, "Recv'd PADI: %P",p_in);
47     poe_dbglog(ses, "Recv'd packet: %P",p_in);
48     newpc = get_con( ntohs(tag->tag_len), tag->tag_data );
49     if(!newpc){
50         
51         newpc = (struct pppoe_con *) malloc(sizeof(struct pppoe_con));
52         memset(newpc , 0, sizeof(struct pppoe_con));
53         
54         newpc->id = pcid++;
55         
56         newpc->key_len = ntohs(p_in->tags[TAG_RELAY_SID]->tag_len);
57         memcpy(newpc->key, p_in->tags[TAG_RELAY_SID]->tag_data, newpc->key_len);
58         memcpy(newpc->client, p_in->addr.sll_addr, ETH_ALEN);
59         
60         memcpy(newpc->server, MAC_BCAST_ADDR, ETH_ALEN);
61         
62         store_con(newpc);
63         
64     }
65
66     ++newpc->ref_count;
67
68     memset(p_in->addr.sll_addr, 0xff, ETH_ALEN);
69
70     p_in->addr.sll_ifindex = ses->remote.sll_ifindex;
71
72     send_disc(ses, p_in);
73     return 0;
74 }
75
76 static int relay_rcv_pkt(struct session* ses,
77                          struct pppoe_packet *p_in,
78                          struct pppoe_packet **p_out){
79     struct pppoe_con *pc;
80     char tag_buf[32];
81     struct pppoe_tag *tag = p_in->tags[TAG_RELAY_SID];
82
83     if( !tag ) return 0;
84
85     pc = get_con(ntohs(tag->tag_len),tag->tag_data);
86
87     if( !pc ) return 0;
88
89     poe_dbglog(ses, "Recv'd packet: %P",p_in);
90
91     if( memcmp(pc->client , p_in->addr.sll_addr , ETH_ALEN ) == 0 ){
92         
93         memcpy(p_in->addr.sll_addr, pc->server, ETH_ALEN);
94         p_in->addr.sll_ifindex = ses->remote.sll_ifindex;
95         
96     }else{
97         if( memcmp(pc->server, MAC_BCAST_ADDR, ETH_ALEN) == 0 ){
98             memcpy(pc->server, p_in->addr.sll_addr, ETH_ALEN);
99         
100         }else if( memcmp(pc->server, p_in->addr.sll_addr, ETH_ALEN) !=0){
101             return 0;
102         }
103         
104         memcpy(p_in->addr.sll_addr, pc->client, ETH_ALEN);
105         p_in->addr.sll_ifindex = ses->local.sll_ifindex;
106         
107         
108     }
109
110
111     send_disc(ses, p_in);
112     return 0;
113 }
114
115 static int relay_rcv_pads(struct session* ses,
116                           struct pppoe_packet *p_in,
117                           struct pppoe_packet **p_out){
118
119     struct pppoe_con *pc;
120     char tag_buf[32];
121     struct pppoe_tag *tag = p_in->tags[TAG_RELAY_SID];
122     struct sockaddr_pppox sp_cl= { AF_PPPOX, PX_PROTO_OE,
123                                    { p_in->hdr->sid, {0,},{0,}}};
124
125     struct sockaddr_pppox sp_sv= { AF_PPPOX, PX_PROTO_OE,
126                                    { p_in->hdr->sid, {0,},{0,}}};
127
128     int ret;
129
130
131     if( !tag ) return 0;
132
133     pc = get_con(ntohs(tag->tag_len),tag->tag_data);
134
135     if( !pc ) return 0;
136
137
138     if(!pc->connected){
139         
140         pc->sv_sock = socket( AF_PPPOX, SOCK_DGRAM, PX_PROTO_OE);
141         if( pc->sv_sock < 0){
142             poe_fatal(ses,"Cannot open PPPoE socket: %i",errno);
143         }
144         
145         pc->cl_sock = socket( AF_PPPOX, SOCK_DGRAM, PX_PROTO_OE);
146         if( pc->cl_sock < 0){
147             poe_fatal(ses,"Cannot open PPPoE socket: %i",errno);
148         }
149         
150         memcpy( sp_sv.sa_addr.pppoe.dev, ses->fwd_name, IFNAMSIZ);
151         memcpy( sp_sv.sa_addr.pppoe.remote, pc->server, ETH_ALEN);
152         
153         ret = connect( pc->sv_sock,
154                        (struct sockaddr*)&sp_sv,
155                        sizeof(struct sockaddr_pppox));
156         if( ret < 0){
157             poe_fatal(ses,"Cannot connect PPPoE socket: %i",errno);
158         }
159         
160         memcpy( sp_cl.sa_addr.pppoe.dev, ses->name, IFNAMSIZ);
161         memcpy( sp_cl.sa_addr.pppoe.remote, pc->client, ETH_ALEN);
162         
163         ret = connect( pc->cl_sock,
164                        (struct sockaddr*)&sp_cl,
165                        sizeof(struct sockaddr_pppox));
166         if( ret < 0){
167             poe_fatal(ses,"Cannot connect PPPoE socket: %i",errno);
168         }
169         
170         
171         ret = ioctl( pc->sv_sock, PPPOEIOCSFWD, &sp_cl);
172         if( ret < 0){
173             poe_fatal(ses,"Cannot set forwarding on PPPoE socket: %i",errno);
174         }
175         
176         ret = ioctl( pc->cl_sock, PPPOEIOCSFWD, &sp_sv);
177         if( ret < 0){
178             poe_fatal(ses,"Cannot set forwarding on PPPoE socket: %i",errno);
179         }
180         
181         pc->connected = 1;
182     }
183
184     poe_info(ses,"PPPoE relay for %E established to %E (sid=%04x)\n",
185              pc->client,pc->server, p_in->hdr->sid);
186
187     return relay_rcv_pkt(ses,p_in,p_out);
188 }
189
190
191 static int relay_rcv_padt(struct session* ses,
192                           struct pppoe_packet *p_in,
193                           struct pppoe_packet **p_out){
194
195     int ret;
196     struct pppoe_con *pc;
197     char tag_buf[32];
198     struct pppoe_tag *tag = p_in->tags[TAG_RELAY_SID];
199
200     if( !tag ) return 0;
201
202     pc = get_con(ntohs(tag->tag_len),tag->tag_data);
203
204     if( !pc ) return 0;
205
206     ret = relay_rcv_pkt(ses,p_in,p_out);
207
208
209     if(pc->cl_sock>0){
210         close(pc->cl_sock);
211     }
212
213     if(pc->sv_sock>0){
214         close(pc->sv_sock);
215     }
216
217     --pc->ref_count;
218     if( pc->ref_count == 0 ){
219         delete_con(pc->key_len, pc->key);
220         
221         free(pc);
222     }
223 }
224
225
226 int relay_init_ses(struct session *ses, char* from, char* to)
227 {
228     int retval = client_init_ses(ses, from);
229
230     if(retval<0) return retval;
231
232     ses->fwd_sock =  socket(PF_PACKET, SOCK_DGRAM, 0);
233     if( ses->fwd_sock < 0 ) {
234         poe_fatal(ses,"Cannot create PF_PACKET socket for PPPoE forwarding\n");
235     }
236
237     /* Verify the device name , construct ses->local */
238     retval = get_sockaddr_ll(to, &ses->remote);
239     if (retval < 0)
240         poe_fatal(ses,"relay_init_ses:get_sockaddr_ll failed %m");
241
242     retval = bind( ses->fwd_sock ,
243                    (struct sockaddr*)&ses->remote,
244                    sizeof(struct sockaddr_ll));
245
246     if( retval < 0 ){
247         poe_fatal(ses,"bind to PF_PACKET socket failed: %m");
248     }
249
250     memcpy(ses->fwd_name, to, IFNAMSIZ);
251     memcpy(ses->name, from, IFNAMSIZ);
252
253
254     ses->init_disc = relay_init_disc;
255     ses->rcv_padi  = relay_rcv_padi;
256     ses->rcv_pado  = relay_rcv_pkt;
257     ses->rcv_padr  = relay_rcv_pkt;
258     ses->rcv_pads  = relay_rcv_pads;
259     ses->rcv_padt  = relay_rcv_padt;
260 }