|
1 /* |
|
2 * Copyright 2004-2006 Intel Corporation |
|
3 * |
|
4 * Licensed under the Apache License, Version 2.0 (the "License"); |
|
5 * you may not use this file except in compliance with the License. |
|
6 * You may obtain a copy of the License at |
|
7 * |
|
8 * http://www.apache.org/licenses/LICENSE-2.0 |
|
9 * |
|
10 * Unless required by applicable law or agreed to in writing, software |
|
11 * distributed under the License is distributed on an "AS IS" BASIS, |
|
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
13 * See the License for the specific language governing permissions and |
|
14 * limitations under the License. |
|
15 */ |
|
16 |
|
17 #ifdef HAVE_CONFIG_H |
|
18 # include <dtn-config.h> |
|
19 #endif |
|
20 |
|
21 // Only works on Linux (for now) |
|
22 #ifdef __linux__ |
|
23 |
|
24 #include <sys/poll.h> |
|
25 #include <stdlib.h> |
|
26 #include <sys/types.h> |
|
27 #include <sys/socket.h> |
|
28 #include <netinet/in.h> |
|
29 #include <net/ethernet.h> |
|
30 #include <netpacket/packet.h> |
|
31 #include <sys/ioctl.h> |
|
32 |
|
33 #include <oasys/io/NetUtils.h> |
|
34 #include <oasys/io/IO.h> |
|
35 #include <oasys/thread/Timer.h> |
|
36 #include <oasys/util/OptParser.h> |
|
37 #include <oasys/util/StringBuffer.h> |
|
38 |
|
39 #include "EthConvergenceLayer.h" |
|
40 #include "bundling/Bundle.h" |
|
41 #include "bundling/BundleEvent.h" |
|
42 #include "bundling/BundleDaemon.h" |
|
43 #include "bundling/BundleList.h" |
|
44 #include "bundling/BundleProtocol.h" |
|
45 #include "contacts/ContactManager.h" |
|
46 #include "contacts/Link.h" |
|
47 |
|
48 using namespace oasys; |
|
49 namespace dtn { |
|
50 |
|
51 struct EthConvergenceLayer::Params EthConvergenceLayer::defaults_; |
|
52 |
|
53 /****************************************************************************** |
|
54 * |
|
55 * EthConvergenceLayer |
|
56 * |
|
57 *****************************************************************************/ |
|
58 |
|
59 EthConvergenceLayer::EthConvergenceLayer() |
|
60 : ConvergenceLayer("EthConvergenceLayer", "eth") |
|
61 { |
|
62 defaults_.beacon_interval_ = 1; |
|
63 } |
|
64 |
|
65 /** |
|
66 * Parse variable args into a parameter structure. |
|
67 */ |
|
68 bool |
|
69 EthConvergenceLayer::parse_params(Params* params, |
|
70 int argc, const char* argv[], |
|
71 const char** invalidp) |
|
72 { |
|
73 oasys::OptParser p; |
|
74 |
|
75 p.addopt(new oasys::UIntOpt("beacon_interval", ¶ms->beacon_interval_)); |
|
76 |
|
77 if (! p.parse(argc, argv, invalidp)) { |
|
78 return false; |
|
79 } |
|
80 |
|
81 return true; |
|
82 } |
|
83 |
|
84 /* |
|
85 * Start listening to, and sending beacons on, the provided interface. |
|
86 * |
|
87 * For now, we support interface strings on the form |
|
88 * string://eth0 |
|
89 * |
|
90 * this should change further down the line to simply be |
|
91 * eth0 |
|
92 * |
|
93 */ |
|
94 |
|
95 bool |
|
96 EthConvergenceLayer::interface_up(Interface* iface, |
|
97 int argc, const char* argv[]) |
|
98 { |
|
99 Params params = EthConvergenceLayer::defaults_; |
|
100 const char *invalid; |
|
101 if (!parse_params(¶ms, argc, argv, &invalid)) { |
|
102 log_err("error parsing interface options: invalid option '%s'", |
|
103 invalid); |
|
104 return false; |
|
105 } |
|
106 |
|
107 // grab the interface name out of the string:// |
|
108 |
|
109 // XXX/jakob - this fugly mess needs to change when we get the |
|
110 // config stuff right |
|
111 const char* if_name=iface->name().c_str()+strlen("string://"); |
|
112 log_info("EthConvergenceLayer::interface_up(%s).", if_name); |
|
113 |
|
114 Receiver* receiver = new Receiver(if_name, ¶ms); |
|
115 receiver->logpathf("/cl/eth"); |
|
116 receiver->start(); |
|
117 iface->set_cl_info(receiver); |
|
118 |
|
119 // remembers the interface beacon object |
|
120 if_beacon_ = new Beacon(if_name, params.beacon_interval_); |
|
121 if_beacon_->logpathf("/cl/eth"); |
|
122 if_beacon_->start(); |
|
123 |
|
124 return true; |
|
125 } |
|
126 |
|
127 bool |
|
128 EthConvergenceLayer::interface_down(Interface* iface) |
|
129 { |
|
130 // XXX/jakob - need to keep track of the Beacon and Receiver threads for each |
|
131 // interface and kill them. |
|
132 // NOTIMPLEMENTED; |
|
133 |
|
134 // xxx/shawn needs to find a way to delete beacon; |
|
135 if_beacon_->set_should_stop(); |
|
136 while (! if_beacon_->is_stopped()) { |
|
137 oasys::Thread::yield(); |
|
138 } |
|
139 delete if_beacon_; |
|
140 |
|
141 Receiver *receiver = (Receiver *)iface->cl_info(); |
|
142 receiver->set_should_stop(); |
|
143 // receiver->interrupt_from_io(); |
|
144 while (! receiver->is_stopped()) { |
|
145 oasys::Thread::yield(); |
|
146 } |
|
147 delete receiver; |
|
148 |
|
149 return true; |
|
150 } |
|
151 |
|
152 bool |
|
153 EthConvergenceLayer::open_contact(const ContactRef& contact) |
|
154 { |
|
155 LinkRef link = contact->link(); |
|
156 ASSERT(link != NULL); |
|
157 ASSERT(!link->isdeleted()); |
|
158 ASSERT(link->cl_info() != NULL); |
|
159 |
|
160 log_debug("EthConvergenceLayer::open_contact: " |
|
161 "opening contact to link *%p", link.object()); |
|
162 |
|
163 // parse out the address from the contact nexthop |
|
164 eth_addr_t addr; |
|
165 if (!EthernetScheme::parse(link->nexthop(), &addr)) { |
|
166 log_err("EthConvergenceLayer::open_contact: " |
|
167 "next hop address '%s' not a valid eth uri", link->nexthop()); |
|
168 return false; |
|
169 } |
|
170 |
|
171 // create a new connection for the contact |
|
172 Sender* sender = new Sender(((EthCLInfo*)link->cl_info())->if_name_, |
|
173 link->contact()); |
|
174 contact->set_cl_info(sender); |
|
175 |
|
176 sender->logpathf("/cl/eth"); |
|
177 |
|
178 BundleDaemon::post(new ContactUpEvent(contact)); |
|
179 return true; |
|
180 } |
|
181 |
|
182 bool |
|
183 EthConvergenceLayer::close_contact(const ContactRef& contact) |
|
184 { |
|
185 Sender* sender = (Sender*)contact->cl_info(); |
|
186 |
|
187 log_info("close_contact *%p", contact.object()); |
|
188 |
|
189 if (sender) { |
|
190 contact->set_cl_info(NULL); |
|
191 delete sender; |
|
192 } |
|
193 |
|
194 return true; |
|
195 } |
|
196 |
|
197 void |
|
198 EthConvergenceLayer::delete_link(const LinkRef& link) |
|
199 { |
|
200 ASSERT(link != NULL); |
|
201 ASSERT(!link->isdeleted()); |
|
202 |
|
203 log_debug("EthConvergenceLayer::delete_link: " |
|
204 "deleting link %s", link->name()); |
|
205 |
|
206 if (link->cl_info() != NULL) { |
|
207 delete link->cl_info(); |
|
208 link->set_cl_info(NULL); |
|
209 } |
|
210 } |
|
211 |
|
212 /** |
|
213 * Send bundles queued up for the contact. |
|
214 */ |
|
215 void |
|
216 EthConvergenceLayer::bundle_queued(const LinkRef& link, const BundleRef& bundle) |
|
217 { |
|
218 ASSERT(link != NULL); |
|
219 ASSERT(!link->isdeleted()); |
|
220 |
|
221 const ContactRef& contact = link->contact(); |
|
222 Sender* sender = (Sender*)contact->cl_info(); |
|
223 if (!sender) { |
|
224 log_crit("send_bundles called on contact *%p with no Sender!!", |
|
225 contact.object()); |
|
226 return; |
|
227 } |
|
228 ASSERT(contact == sender->contact_); |
|
229 |
|
230 sender->send_bundle(bundle); |
|
231 } |
|
232 |
|
233 bool |
|
234 EthConvergenceLayer::is_queued(const LinkRef& contact, Bundle* bundle) |
|
235 { |
|
236 (void)contact; |
|
237 (void)bundle; |
|
238 |
|
239 /// The Ethernet convergence layer does not maintain an output queue. |
|
240 return false; |
|
241 } |
|
242 |
|
243 /****************************************************************************** |
|
244 * |
|
245 * EthConvergenceLayer::Receiver |
|
246 * |
|
247 *****************************************************************************/ |
|
248 EthConvergenceLayer::Receiver::Receiver(const char* if_name, |
|
249 EthConvergenceLayer::Params* params) |
|
250 : Logger("EthConvergenceLayer::Receiver", "/dtn/cl/eth/receiver"), |
|
251 Thread("EthConvergenceLayer::Receiver") |
|
252 { |
|
253 memset(if_name_,0, IFNAMSIZ); |
|
254 strcpy(if_name_,if_name); |
|
255 Thread::flags_ |= INTERRUPTABLE; |
|
256 (void)params; |
|
257 } |
|
258 |
|
259 void |
|
260 EthConvergenceLayer::Receiver::process_data(u_char* bp, size_t len) |
|
261 { |
|
262 Bundle* bundle = NULL; |
|
263 EthCLHeader ethclhdr; |
|
264 size_t bundle_len; |
|
265 struct ether_header* ethhdr=(struct ether_header*)bp; |
|
266 |
|
267 log_debug("Received DTN packet on interface %s, %zu.",if_name_, len); |
|
268 |
|
269 // copy in the ethcl header. |
|
270 if (len < sizeof(EthCLHeader)) { |
|
271 log_err("process_data: " |
|
272 "incoming packet too small (len = %zu)", len); |
|
273 return; |
|
274 } |
|
275 memcpy(ðclhdr, bp+sizeof(struct ether_header), sizeof(EthCLHeader)); |
|
276 |
|
277 // check for valid magic number and version |
|
278 if (ethclhdr.version != ETHCL_VERSION) { |
|
279 log_warn("remote sent version %d, expected version %d " |
|
280 "-- disconnecting.", ethclhdr.version, ETHCL_VERSION); |
|
281 return; |
|
282 } |
|
283 |
|
284 if(ethclhdr.type == ETHCL_BEACON) { |
|
285 ContactManager* cm = BundleDaemon::instance()->contactmgr(); |
|
286 |
|
287 char bundles_string[60]; |
|
288 memset(bundles_string,0,60); |
|
289 EthernetScheme::to_string(ðhdr->ether_shost[0], |
|
290 bundles_string); |
|
291 char next_hop_string[50], *ptr; |
|
292 memset(next_hop_string,0,50); |
|
293 ptr = strrchr(bundles_string, '/'); |
|
294 strcpy(next_hop_string, ptr+1); |
|
295 |
|
296 ConvergenceLayer* cl = ConvergenceLayer::find_clayer("eth"); |
|
297 EndpointID remote_eid(bundles_string); |
|
298 |
|
299 LinkRef link = cm->find_link_to(cl, |
|
300 next_hop_string, |
|
301 remote_eid, |
|
302 Link::OPPORTUNISTIC); |
|
303 |
|
304 if(link == NULL) { |
|
305 log_info("EthConvergenceLayer::Receiver::process_data: " |
|
306 "Discovered next_hop %s on interface %s.", |
|
307 next_hop_string, if_name_); |
|
308 |
|
309 // registers a new contact with the routing layer |
|
310 link = cm->new_opportunistic_link(cl, |
|
311 next_hop_string, |
|
312 EndpointID(bundles_string)); |
|
313 |
|
314 if (link == NULL) { |
|
315 log_debug("EthConvergenceLayer::Receiver::process_data: " |
|
316 "failed to create new opportunistic link"); |
|
317 return; |
|
318 } |
|
319 |
|
320 oasys::ScopeLock l(link->lock(), "EthConvergenceLayer::Receiver"); |
|
321 // It is doubtful that the link would be deleted already, |
|
322 // but check just in case. |
|
323 if (link->isdeleted()) { |
|
324 log_warn("EthConvergenceLayer::Receiver::process_data: " |
|
325 "link %s deleted before fully initialized", |
|
326 link->name()); |
|
327 return; |
|
328 } |
|
329 ASSERT(link->cl_info() == NULL); |
|
330 link->set_cl_info(new EthCLInfo(if_name_)); |
|
331 l.unlock(); |
|
332 } |
|
333 |
|
334 ASSERT(link != NULL); |
|
335 oasys::ScopeLock l(link->lock(), "EthConvergenceLayer::Receiver"); |
|
336 |
|
337 if (link->isdeleted()) { |
|
338 log_warn("EthConvergenceLayer::Receiver::process_data: " |
|
339 "link %s already deleted", link->name()); |
|
340 return; |
|
341 } |
|
342 |
|
343 ASSERT(link->cl_info() != NULL); |
|
344 ASSERT(strcmp(((EthCLInfo*)link->cl_info())->if_name_, if_name_) == 0); |
|
345 |
|
346 if(!link->isavailable()) { |
|
347 log_info("EthConvergenceLayer::Receiver::process_data: " |
|
348 "Got beacon for previously unavailable link %s", |
|
349 link->name()); |
|
350 |
|
351 // XXX/demmer something should be done here to kick the link... |
|
352 log_err("XXX/demmer do something about link availability"); |
|
353 } |
|
354 |
|
355 /** |
|
356 * If there already is a timer for this link, cancel it, which |
|
357 * will delete it when it bubbles to the top of the timer |
|
358 * queue. Then create a new timer. |
|
359 */ |
|
360 BeaconTimer *timer = ((EthCLInfo*)link->cl_info())->timer; |
|
361 if (timer) |
|
362 timer->cancel(); |
|
363 |
|
364 timer = new BeaconTimer(next_hop_string); |
|
365 timer->schedule_in(ETHCL_BEACON_TIMEOUT_INTERVAL); |
|
366 |
|
367 ((EthCLInfo*)link->cl_info())->timer = timer; |
|
368 |
|
369 l.unlock(); |
|
370 } |
|
371 else if(ethclhdr.type == ETHCL_BUNDLE) { |
|
372 // infer the bundle length based on the packet length minus the |
|
373 // eth cl header |
|
374 bundle_len = len - sizeof(EthCLHeader) - sizeof(struct ether_header); |
|
375 |
|
376 log_debug("process_data: got ethcl header -- bundle id %d, length %zu", |
|
377 ntohl(ethclhdr.bundle_id), bundle_len); |
|
378 |
|
379 // skip past the cl header |
|
380 bp += (sizeof(EthCLHeader) + sizeof(struct ether_header)); |
|
381 len -= (sizeof(EthCLHeader) + sizeof(struct ether_header)); |
|
382 |
|
383 bundle = new Bundle(); |
|
384 bool complete = false; |
|
385 int cc = BundleProtocol::consume(bundle, bp, len, &complete); |
|
386 |
|
387 if (cc < 0) { |
|
388 log_err("process_data: bundle protocol error"); |
|
389 delete bundle; |
|
390 return; |
|
391 } |
|
392 |
|
393 if (!complete) { |
|
394 log_err("process_data: incomplete bundle"); |
|
395 delete bundle; |
|
396 return; |
|
397 } |
|
398 |
|
399 log_debug("process_data: new bundle id %d arrival, bundle length %zu", |
|
400 bundle->bundleid(), bundle_len); |
|
401 |
|
402 BundleDaemon::post( |
|
403 new BundleReceivedEvent(bundle, EVENTSRC_PEER, |
|
404 bundle_len, EndpointID::NULL_EID())); |
|
405 } |
|
406 } |
|
407 |
|
408 void |
|
409 EthConvergenceLayer::Receiver::run() |
|
410 { |
|
411 int sock; |
|
412 int cc; |
|
413 struct sockaddr_ll iface; |
|
414 unsigned char buffer[MAX_ETHER_PACKET]; |
|
415 |
|
416 if((sock = socket(PF_PACKET,SOCK_RAW, htons(ETHERTYPE_DTN))) < 0) { |
|
417 perror("socket"); |
|
418 log_err("EthConvergenceLayer::Receiver::run() " |
|
419 "Couldn't open socket."); |
|
420 exit(1); |
|
421 } |
|
422 |
|
423 // figure out the interface index of the device with name if_name_ |
|
424 struct ifreq req; |
|
425 strcpy(req.ifr_name, if_name_); |
|
426 ioctl(sock, SIOCGIFINDEX, &req); |
|
427 |
|
428 memset(&iface, 0, sizeof(iface)); |
|
429 iface.sll_family=AF_PACKET; |
|
430 iface.sll_protocol=htons(ETHERTYPE_DTN); |
|
431 iface.sll_ifindex=req.ifr_ifindex; |
|
432 |
|
433 if (bind(sock, (struct sockaddr *) &iface, sizeof(iface)) == -1) { |
|
434 perror("bind"); |
|
435 exit(1); |
|
436 } |
|
437 |
|
438 log_warn("Reading from socket..."); |
|
439 while(true) { |
|
440 cc=read (sock, buffer, MAX_ETHER_PACKET); |
|
441 if(cc<=0) { |
|
442 perror("EthConvergenceLayer::Receiver::run()"); |
|
443 exit(1); |
|
444 } |
|
445 struct ether_header* hdr=(struct ether_header*)buffer; |
|
446 |
|
447 if(ntohs(hdr->ether_type)==ETHERTYPE_DTN) { |
|
448 process_data(buffer, cc); |
|
449 } |
|
450 else if(ntohs(hdr->ether_type)!=0x800) |
|
451 { |
|
452 log_err("Got non-DTN packet in Receiver, type %4X.", |
|
453 ntohs(hdr->ether_type)); |
|
454 // exit(1); |
|
455 } |
|
456 |
|
457 if(should_stop()) |
|
458 break; |
|
459 } |
|
460 } |
|
461 |
|
462 /****************************************************************************** |
|
463 * |
|
464 * EthConvergenceLayer::Sender |
|
465 * |
|
466 *****************************************************************************/ |
|
467 |
|
468 /** |
|
469 * Constructor for the active connection side of a connection. |
|
470 */ |
|
471 EthConvergenceLayer::Sender::Sender(char* if_name, |
|
472 const ContactRef& contact) |
|
473 : Logger("EthConvergenceLayer::Sender", "/dtn/cl/eth/sender"), |
|
474 contact_(contact.object(), "EthConvergenceLayer::Sender") |
|
475 { |
|
476 struct ifreq req; |
|
477 struct sockaddr_ll iface; |
|
478 LinkRef link = contact->link(); |
|
479 |
|
480 memset(src_hw_addr_.octet, 0, 6); // determined in Sender::run() |
|
481 EthernetScheme::parse(link->nexthop(), &dst_hw_addr_); |
|
482 |
|
483 strcpy(if_name_, if_name); |
|
484 sock_ = 0; |
|
485 |
|
486 memset(&req, 0, sizeof(req)); |
|
487 memset(&iface, 0, sizeof(iface)); |
|
488 |
|
489 // Get and bind a RAW socket for this contact. XXX/jakob - seems |
|
490 // like it'd be enough with one socket per interface, not one per |
|
491 // contact. figure this out some time. |
|
492 if((sock_ = socket(AF_PACKET,SOCK_RAW, htons(ETHERTYPE_DTN))) < 0) { |
|
493 perror("socket"); |
|
494 exit(1); |
|
495 } |
|
496 |
|
497 // get the interface name from the contact info |
|
498 strcpy(req.ifr_name, if_name_); |
|
499 |
|
500 // ifreq the interface index for binding the socket |
|
501 ioctl(sock_, SIOCGIFINDEX, &req); |
|
502 |
|
503 iface.sll_family=AF_PACKET; |
|
504 iface.sll_protocol=htons(ETHERTYPE_DTN); |
|
505 iface.sll_ifindex=req.ifr_ifindex; |
|
506 |
|
507 // store away the ethernet address of the device in question |
|
508 if(ioctl(sock_, SIOCGIFHWADDR, &req)) |
|
509 { |
|
510 perror("ioctl"); |
|
511 exit(1); |
|
512 } |
|
513 memcpy(src_hw_addr_.octet,req.ifr_hwaddr.sa_data,6); |
|
514 |
|
515 if (bind(sock_, (struct sockaddr *) &iface, sizeof(iface)) == -1) { |
|
516 perror("bind"); |
|
517 exit(1); |
|
518 } |
|
519 } |
|
520 |
|
521 /* |
|
522 * Send one bundle. |
|
523 */ |
|
524 bool |
|
525 EthConvergenceLayer::Sender::send_bundle(const BundleRef& bundle) |
|
526 { |
|
527 int cc; |
|
528 struct iovec iov[3]; |
|
529 |
|
530 EthCLHeader ethclhdr; |
|
531 struct ether_header hdr; |
|
532 |
|
533 memset(iov,0,sizeof(iov)); |
|
534 |
|
535 // iovec slot 0 holds the ethernet header |
|
536 |
|
537 iov[0].iov_base = (char*)&hdr; |
|
538 iov[0].iov_len = sizeof(struct ether_header); |
|
539 |
|
540 // write the ethernet header |
|
541 |
|
542 memcpy(hdr.ether_dhost,dst_hw_addr_.octet,6); |
|
543 memcpy(hdr.ether_shost,src_hw_addr_.octet,6); // Sender::hw_addr |
|
544 hdr.ether_type=htons(ETHERTYPE_DTN); |
|
545 |
|
546 // iovec slot 1 for the eth cl header |
|
547 |
|
548 iov[1].iov_base = (char*)ðclhdr; |
|
549 iov[1].iov_len = sizeof(EthCLHeader); |
|
550 |
|
551 // write the ethcl header |
|
552 |
|
553 ethclhdr.version = ETHCL_VERSION; |
|
554 ethclhdr.type = ETHCL_BUNDLE; |
|
555 ethclhdr.bundle_id = htonl(bundle->bundleid()); |
|
556 |
|
557 // iovec slot 2 for the bundle |
|
558 BlockInfoVec* blocks = bundle->xmit_blocks()->find_blocks(contact_->link()); |
|
559 ASSERT(blocks != NULL); |
|
560 |
|
561 bool complete = false; |
|
562 size_t total_len = BundleProtocol::produce(bundle.object(), blocks, |
|
563 buf_, 0, sizeof(buf_), |
|
564 &complete); |
|
565 if (!complete) { |
|
566 size_t formatted_len = BundleProtocol::total_length(blocks); |
|
567 log_err("send_bundle: bundle too big (%zu > %u)", |
|
568 formatted_len, MAX_ETHER_PACKET); |
|
569 return -1; |
|
570 } |
|
571 |
|
572 iov[2].iov_base = (char *)buf_; |
|
573 iov[2].iov_len = total_len; |
|
574 |
|
575 // We're done assembling the packet. Now write the whole thing to |
|
576 // the socket! |
|
577 log_info("Sending bundle out interface %s",if_name_); |
|
578 |
|
579 cc=IO::writevall(sock_, iov, 3); |
|
580 if(cc<0) { |
|
581 perror("send"); |
|
582 log_err("Send failed!\n"); |
|
583 } |
|
584 log_info("Sent packet, size: %d",cc ); |
|
585 |
|
586 // move the bundle off the link queue and onto the inflight queue |
|
587 contact_->link()->del_from_queue(bundle, total_len); |
|
588 contact_->link()->add_to_inflight(bundle, total_len); |
|
589 |
|
590 // check that we successfully wrote it all |
|
591 bool ok; |
|
592 int total = sizeof(EthCLHeader) + sizeof(struct ether_header) + total_len; |
|
593 if (cc != total) { |
|
594 log_err("send_bundle: error writing bundle (wrote %d/%d): %s", |
|
595 cc, total, strerror(errno)); |
|
596 ok = false; |
|
597 } else { |
|
598 // cons up a transmission event and pass it to the router |
|
599 // since this is an unreliable protocol, acked_len = 0, and |
|
600 // ack = false |
|
601 BundleDaemon::post( |
|
602 new BundleTransmittedEvent(bundle.object(), contact_,contact_->link(), |
|
603 total_len, false)); |
|
604 ok = true; |
|
605 } |
|
606 |
|
607 return ok; |
|
608 } |
|
609 |
|
610 EthConvergenceLayer::Beacon::Beacon(const char* if_name, |
|
611 unsigned int beacon_interval) |
|
612 : Logger("EthConvergenceLayer::Beacon", "/dtn/cl/eth/beacon"), |
|
613 Thread("EthConvergenceLayer::Beacon") |
|
614 { |
|
615 Thread::flags_ |= INTERRUPTABLE; |
|
616 memset(if_name_, 0, IFNAMSIZ); |
|
617 strcpy(if_name_, if_name); |
|
618 beacon_interval_ = beacon_interval; |
|
619 } |
|
620 |
|
621 void EthConvergenceLayer::Beacon::run() |
|
622 { |
|
623 // ethernet broadcast address |
|
624 char bcast_mac_addr[6]={0xff,0xff,0xff,0xff,0xff,0xff}; |
|
625 |
|
626 struct ether_header hdr; |
|
627 struct sockaddr_ll iface; |
|
628 EthCLHeader ethclhdr; |
|
629 |
|
630 int sock,cc; |
|
631 struct iovec iov[2]; |
|
632 |
|
633 memset(&hdr,0,sizeof(hdr)); |
|
634 memset(ðclhdr,0,sizeof(ethclhdr)); |
|
635 memset(&iface,0,sizeof(iface)); |
|
636 |
|
637 ethclhdr.version = ETHCL_VERSION; |
|
638 ethclhdr.type = ETHCL_BEACON; |
|
639 |
|
640 hdr.ether_type=htons(ETHERTYPE_DTN); |
|
641 |
|
642 // iovec slot 0 holds the ethernet header |
|
643 iov[0].iov_base = (char*)&hdr; |
|
644 iov[0].iov_len = sizeof(struct ether_header); |
|
645 |
|
646 // use iovec slot 1 for the eth cl header |
|
647 iov[1].iov_base = (char*)ðclhdr; |
|
648 iov[1].iov_len = sizeof(EthCLHeader); |
|
649 |
|
650 /* |
|
651 Get ourselves a raw socket, and configure it. |
|
652 */ |
|
653 if((sock = socket(AF_PACKET,SOCK_RAW, htons(ETHERTYPE_DTN))) < 0) { |
|
654 perror("socket"); |
|
655 exit(1); |
|
656 } |
|
657 |
|
658 struct ifreq req; |
|
659 strcpy(req.ifr_name, if_name_); |
|
660 if(ioctl(sock, SIOCGIFINDEX, &req)) |
|
661 { |
|
662 perror("ioctl"); |
|
663 exit(1); |
|
664 } |
|
665 |
|
666 iface.sll_ifindex=req.ifr_ifindex; |
|
667 |
|
668 if(ioctl(sock, SIOCGIFHWADDR, &req)) |
|
669 { |
|
670 perror("ioctl"); |
|
671 exit(1); |
|
672 } |
|
673 |
|
674 memcpy(hdr.ether_dhost,bcast_mac_addr,6); |
|
675 memcpy(hdr.ether_shost,req.ifr_hwaddr.sa_data,6); |
|
676 |
|
677 log_info("Interface %s has interface number %d.",if_name_,req.ifr_ifindex); |
|
678 |
|
679 iface.sll_family=AF_PACKET; |
|
680 iface.sll_protocol=htons(ETHERTYPE_DTN); |
|
681 |
|
682 if (bind(sock, (struct sockaddr *) &iface, sizeof(iface)) == -1) { |
|
683 perror("bind"); |
|
684 exit(1); |
|
685 } |
|
686 |
|
687 /* |
|
688 * Send the beacon on the socket every beacon_interval_ second. |
|
689 */ |
|
690 while(1) { |
|
691 sleep(beacon_interval_); |
|
692 |
|
693 if (should_stop()) |
|
694 break; |
|
695 |
|
696 log_debug("Sent beacon out interface %s.\n",if_name_ ); |
|
697 |
|
698 cc=IO::writevall(sock, iov, 2); |
|
699 if(cc<0) { |
|
700 perror("send beacon"); |
|
701 log_err("Send beacon failed!\n"); |
|
702 } |
|
703 } |
|
704 } |
|
705 |
|
706 EthConvergenceLayer::BeaconTimer::BeaconTimer(char * next_hop) |
|
707 : Logger("EthConvergenceLayer::BeaconTimer", "/dtn/cl/eth/beacontimer") |
|
708 { |
|
709 next_hop_=(char*)malloc(strlen(next_hop)+1); |
|
710 strcpy(next_hop_, next_hop); |
|
711 } |
|
712 |
|
713 EthConvergenceLayer::BeaconTimer::~BeaconTimer() |
|
714 { |
|
715 free(next_hop_); |
|
716 } |
|
717 |
|
718 void |
|
719 EthConvergenceLayer::BeaconTimer::timeout(const struct timeval& now) |
|
720 { |
|
721 ContactManager* cm = BundleDaemon::instance()->contactmgr(); |
|
722 ConvergenceLayer* cl = ConvergenceLayer::find_clayer("eth"); |
|
723 LinkRef link = cm->find_link_to(cl, next_hop_); |
|
724 |
|
725 (void)now; |
|
726 |
|
727 log_info("Neighbor %s timer expired.",next_hop_); |
|
728 |
|
729 if(link == NULL) { |
|
730 log_warn("No link for next_hop %s.",next_hop_); |
|
731 } |
|
732 else if(link->isopen()) { |
|
733 BundleDaemon::post( |
|
734 new LinkStateChangeRequest(link, Link::CLOSED, |
|
735 ContactDownEvent::BROKEN)); |
|
736 } |
|
737 else { |
|
738 log_warn("next_hop %s unexpectedly not open",next_hop_); |
|
739 } |
|
740 } |
|
741 |
|
742 Timer * |
|
743 EthConvergenceLayer::BeaconTimer::copy() |
|
744 { |
|
745 return new BeaconTimer(*this); |
|
746 } |
|
747 |
|
748 } // namespace dtn |
|
749 |
|
750 #endif // __linux |