1
2 #include "protoDetour.h"
3 #include "protoSocket.h"
4 #include "protoRouteMgr.h" // for ifAddr/ifIndex mapping
5
6 #include <sys/types.h>
7 #include <sys/socket.h>
8 #include <netinet/in.h>
9 #include <string.h>
10 #include <arpa/inet.h>
11 #include <stdio.h>
12 #include <string.h>
13 #include <unistd.h>
14 #include <fcntl.h> // for fcntl(), etc
15
16 class BsdDetour : public ProtoDetour
17 {
18 public:
19 BsdDetour();
20 ~BsdDetour();
21
22 bool Open(int hookFlags = 0,
23 const ProtoAddress& srcFilterAddr = PROTO_ADDR_NONE,
24 unsigned int srcFilterMask = 0,
25 const ProtoAddress& dstFilterAddr = PROTO_ADDR_NONE,
26 unsigned int dstFilterMask = 0,
27 int dscpValue = -1);
28 void Close();
29 bool Recv(char* buffer,
30 unsigned int& numBytes,
31 Direction* direction = NULL,
32 ProtoAddress* srcMac = NULL,
33 unsigned int* ifIndex = NULL);
34
35 bool Allow(const char* buffer, unsigned int numBytes);
36 bool Drop();
37 bool Inject(const char* buffer, unsigned int numBytes);
38
39 virtual bool SetMulticastInterface(const char* interfaceName);
40
41 enum {DIVERT_PORT = 2998};
42
43 private:
44 enum Action
45 {
46 INSTALL,
47 DELETE
48 };
49 bool SetIPFirewall(Action action,
50 int hookFlags ,
51 const ProtoAddress& srcFilterAddr,
52 unsigned int srcFilterMask,
53 const ProtoAddress& dstFilterAddr,
54 unsigned int dstFilterMask);
55
56 ProtoAddress pkt_addr; // iface IP addr for inbound, unspecified for outbound
57
58 int domain; // AF_INET or AF_INET6
59 int hook_flags;
60 ProtoAddress src_filter_addr;
61 unsigned int src_filter_mask;
62 ProtoAddress dst_filter_addr;
63 unsigned int dst_filter_mask;
64
65 unsigned short rule_number[3];
66 unsigned int rule_count;
67 bool inject_only;
68
69
70 class AddressListItem : public ProtoTree::Item
71 {
72 public:
73 AddressListItem(const ProtoAddress& addr, unsigned int ifIndex);
74
GetInterfaceIndex() const75 unsigned int GetInterfaceIndex() const
76 {return if_index;}
77
78 // ProtoTree::Item overrides
GetKey() const79 const char* GetKey() const
80 {return if_addr.GetRawHostAddress();}
GetKeysize() const81 unsigned int GetKeysize() const
82 {return (if_addr.GetLength() << 3);}
83
84 private:
85 ProtoAddress if_addr;
86 unsigned int if_index;
87 };
88
89 ProtoTree if_addr_list; // for if_addr -> ifIndex lookup
90
91 }; // end class BsdDetour
92
Create()93 ProtoDetour* ProtoDetour::Create()
94 {
95 return static_cast<ProtoDetour*>(new BsdDetour());
96 } // end ProtoDetour::Create(), (void*)nl_head
97
AddressListItem(const ProtoAddress & ifAddr,unsigned int ifIndex)98 BsdDetour::AddressListItem::AddressListItem(const ProtoAddress& ifAddr, unsigned int ifIndex)
99 : if_addr(ifAddr), if_index(ifIndex)
100 {
101 }
102
BsdDetour()103 BsdDetour::BsdDetour()
104 : hook_flags(0), rule_count(0), inject_only(false)
105 {
106 memset(&pkt_addr, 0, sizeof(struct sockaddr_storage));
107 }
108
~BsdDetour()109 BsdDetour::~BsdDetour()
110 {
111 Close();
112 }
113
SetIPFirewall(Action action,int hookFlags,const ProtoAddress & srcFilterAddr,unsigned int srcFilterMask,const ProtoAddress & dstFilterAddr,unsigned int dstFilterMask)114 bool BsdDetour::SetIPFirewall(Action action,
115 int hookFlags ,
116 const ProtoAddress& srcFilterAddr,
117 unsigned int srcFilterMask,
118 const ProtoAddress& dstFilterAddr,
119 unsigned int dstFilterMask)
120 {
121 // 1) IPv4 or IPv6 address family?
122 const char* cmd;
123 if (srcFilterAddr.GetType() != dstFilterAddr.GetType())
124 {
125 PLOG(PL_ERROR, "BsdDetour::SetIPFirewall() error: inconsistent src/dst filter addr families\n");
126 return false;
127 }
128 else if (ProtoAddress::IPv4 == srcFilterAddr.GetType())
129 {
130 cmd = "/sbin/ipfw"; // IPv4 ipfw
131 }
132 else if (ProtoAddress::IPv6 == srcFilterAddr.GetType())
133 {
134 cmd = "/sbin/ipfw"; // IPv6 ipfw (ipfw2 does both, in theory)
135 }
136 else
137 {
138 PLOG(PL_ERROR, "BsdDetour::SetIPFirewall() error: unspecified filter addr family\n");
139 return false;
140 }
141 // 2) For (DELETE == action) re-interpret hookFlags value as rule number
142 if (DELETE == action) hookFlags++;
143 // 3) For which firewall hooks?
144 while (0 != hookFlags)
145 {
146 // Make and install "iptables" firewall rules
147 const size_t RULE_MAX = 511;
148 char rule[RULE_MAX+1];
149
150 if (INSTALL == action)
151 {
152 if (rule_count > 2)
153 {
154 PLOG(PL_ERROR, "BsdDetour::SetIPFirewall() max ruleCount exceeded!\n");
155 return false;
156 }
157
158 const char* target;
159 if (0 != (hookFlags & OUTPUT))
160 {
161 target = "out";
162 hookFlags &= ~OUTPUT;
163 }
164 else if (0 != (hookFlags & INPUT))
165 {
166 target = "in";
167 hookFlags &= ~INPUT;
168 }
169 else if (0 != (hookFlags & FORWARD))
170 {
171 target = "via any";
172 hookFlags &= ~FORWARD;
173 }
174 else
175 {
176 break; // all flags have been processed
177 }
178
179 // cmd = "ipfw" or "ip6fw"
180 const char* f = (ProtoAddress::IPv4 == srcFilterAddr.GetType()) ?
181 "ip" : "ipv6";
182 sprintf(rule, "%s add divert %hu %s ", cmd, (UINT16)DIVERT_PORT, f);
183 if (0 != srcFilterMask)
184 {
185 strcat(rule, "from ");
186 size_t len = strlen(rule);
187 if (!srcFilterAddr.GetHostString(rule+len, RULE_MAX - len))
188 {
189 PLOG(PL_ERROR, "BsdDetour::SetIPFirewall() error: bad source addr filter\n");
190 return false;
191 }
192 len = strlen(rule);
193 sprintf(rule+len, "/%u ", srcFilterMask);
194 }
195 else
196 {
197 size_t len = strlen(rule);
198 sprintf(rule+len, "from any ");
199 }
200 if (0 != dstFilterMask)
201 {
202 strcat(rule, "to ");
203 size_t len = strlen(rule);
204 if (!dstFilterAddr.GetHostString(rule+len, RULE_MAX - len))
205 {
206 PLOG(PL_ERROR, "BsdDetour::SetIPFirewall() error: bad destination addr filter\n");
207 return false;
208 }
209 len = strlen(rule);
210 sprintf(rule+len, "/%u ", dstFilterMask);
211 }
212 else
213 {
214 size_t len = strlen(rule);
215 sprintf(rule+len, "to any ");
216 }
217
218 // target = "in", "out", or "via any"
219 strcat(rule, target);
220 }
221 else // (DELETE == action)
222 {
223 sprintf(rule, "%s delete %d\n", cmd, hookFlags - 1);
224 hookFlags = 0;
225 }
226
227 // Add redirection so we can get stderr result
228 strcat(rule, " 2>&1");
229
230 FILE* p = popen(rule, "r");
231 if (NULL != p)
232 {
233 char feedback[256];
234 fread(feedback, 1, 256, p);
235 char* ptr = strchr(feedback, '\n');
236 if (NULL != ptr) *ptr = '\0';
237 feedback[255] = '\0';
238 if (0 == pclose(p))
239 {
240 if (INSTALL == action)
241 {
242 // Add firewall rule number to list for delete on close
243 if (1 != sscanf(feedback, "%05hu\n", rule_number+rule_count))
244 {
245 PLOG(PL_ERROR, "BsdDetour::SetIPFirewall() warning: couldn't record firewall rule number\n");
246 return true;
247 }
248 rule_count++;
249 }
250 }
251 else
252 {
253 PLOG(PL_ERROR, "BsdDetour::SetIPFirewall() \"%s\" error: %s\n",
254 rule, feedback);
255 return false;
256 }
257 }
258 else
259 {
260 PLOG(PL_ERROR, "BsdDetour::SetIPFirewall() error: popen(%s): %s\n",
261 rule, GetErrorString());
262 return false;
263 }
264 } // end while (0 != hookFlags)
265 return true;
266 } // end BsdDetour::SetIPFirewall()
267
Open(int hookFlags,const ProtoAddress & srcFilterAddr,unsigned int srcFilterMask,const ProtoAddress & dstFilterAddr,unsigned int dstFilterMask,int)268 bool BsdDetour::Open(int hookFlags,
269 const ProtoAddress& srcFilterAddr,
270 unsigned int srcFilterMask,
271 const ProtoAddress& dstFilterAddr,
272 unsigned int dstFilterMask,
273 int /*dscpValue*/) // TBD - support DSCP
274 {
275 if (IsOpen()) Close();
276
277 // Check for "inject-only" mode and "rewire appropriately
278 inject_only = false;
279 if (0 != (hookFlags & INJECT))
280 {
281 // 0) Open raw socket for optional packet injection use
282 //if (0 > (raw_fd = socket(domain, SOCK_RAW, IPPROTO_RAW)))
283 if (0 > (descriptor = socket(PF_INET, SOCK_RAW, IPPROTO_RAW))) // or can we IPv6 on an AF_INET/HDRINCL socket?
284 {
285 PLOG(PL_ERROR, "BsdDetour::Open() socket(IPPROTO_RAW) error: %s\n",
286 GetErrorString());
287 return false;
288 }
289 domain = AF_INET;
290 if (AF_INET == domain)
291 {
292 // Note: no IP_HDRINCL for IPv6 raw sockets ?
293 int enable = 1;
294 if (setsockopt(descriptor, IPPROTO_IP, IP_HDRINCL, &enable, sizeof(enable)))
295 {
296 PLOG(PL_ERROR, "BsdDetour::Open() setsockopt(IP_HDRINCL) error: %s\n",
297 GetErrorString());
298 Close();
299 return false;
300 }
301 }
302 ProtoDetour::Open();
303 StopInputNotification();
304
305 // Set to non-blocking for our purposes (TBD) Add a SetBlocking() method
306 if(-1 == fcntl(descriptor, F_SETFL, fcntl(descriptor, F_GETFL, 0) | O_NONBLOCK))
307 {
308 PLOG(PL_ERROR, "BsdDetour::Open() fcntl(F_SETFL(O_NONBLOCK)) error: %s\n", GetErrorString());
309 Close();
310 return false;
311 }
312 inject_only = true;
313 return true;
314 }
315
316
317 if (srcFilterAddr.GetType() != dstFilterAddr.GetType())
318 {
319 PLOG(PL_ERROR, "BsdDetour::Open() error: inconsistent src/dst filter addr families\n");
320 return false;
321 }
322 else if (ProtoAddress::IPv4 == srcFilterAddr.GetType())
323
324 {
325 domain = AF_INET;
326 }
327 else if (ProtoAddress::IPv6 == srcFilterAddr.GetType())
328 {
329 domain = AF_INET6;
330 }
331 else
332 {
333 PLOG(PL_ERROR, "BsdDetour::Open() error: unspecified filter addr family\n");
334 return false;
335 }
336
337 // Setup ipfw rule(s) ...
338 // Save parameters for firewall rule removal
339 hook_flags = hookFlags;
340 src_filter_addr = srcFilterAddr;
341 src_filter_mask = srcFilterMask;
342 dst_filter_addr = dstFilterAddr;
343 dst_filter_mask = dstFilterMask;
344 if (0 != hookFlags)
345 {
346 if (!SetIPFirewall(INSTALL, hookFlags,
347 srcFilterAddr, srcFilterMask,
348 dstFilterAddr, dstFilterMask))
349 {
350 PLOG(PL_ERROR, "BsdDetour::Open() error: couldn't install firewall rules\n");
351 Close();
352 return false;
353 }
354 }
355
356 // Open a divert socket ...
357 if ((descriptor = socket(domain, SOCK_RAW, IPPROTO_DIVERT)) < 0)
358 {
359 PLOG(PL_ERROR, "BsdDetour::Open() socket() error: %s\n", GetErrorString());
360 Close();
361 return false;
362 }
363
364 // Bind to our ipfw divert "port"
365 UINT16 ipfwPort = DIVERT_PORT; // (TBD) manage port numbers dynamically?
366 struct sockaddr_storage socketAddr;
367 socklen_t addrSize;
368 if (AF_INET == domain)
369 {
370 addrSize = sizeof(struct sockaddr_in);
371 memset((char*)&socketAddr, 0, sizeof(struct sockaddr_in));
372 ((struct sockaddr_in*)&socketAddr)->sin_family = AF_INET;
373 ((struct sockaddr_in*)&socketAddr)->sin_port = htons(ipfwPort);
374 }
375 else // if (AF_INET6 == domain)
376 {
377 addrSize = sizeof(struct sockaddr_in6);
378 memset((char*)&socketAddr, 0, sizeof(struct sockaddr_in6));
379 ((struct sockaddr_in6*)&socketAddr)->sin6_family = AF_INET6;
380 ((struct sockaddr_in6*)&socketAddr)->sin6_port = htons(ipfwPort);
381 ((struct sockaddr_in6*)&socketAddr)->sin6_flowinfo = 0;
382 }
383 if (bind(descriptor, (struct sockaddr*)&socketAddr, addrSize) < 0)
384 {
385 PLOG(PL_ERROR, "BsdDetour::Open() bind() error: %s\n", GetErrorString());
386 Close();
387 return false;
388 }
389
390 if (!ProtoDetour::Open())
391 {
392 PLOG(PL_ERROR, "BsdDetour::Open() ProtoChannel::Open() error\n");
393 Close();
394 return false;
395 }
396
397 // Set up ProtoAddressList for lookup of ifAddr -> ifIndex
398 ProtoRouteMgr* rtMgr = ProtoRouteMgr::Create();
399 if (NULL == rtMgr)
400 {
401 PLOG(PL_ERROR, "BsdDetour::Open(): ProtoRouteMgr::Create() error: %s\n", GetErrorString());
402 Close();
403 return false;
404 }
405 if (!rtMgr->Open())
406 {
407 PLOG(PL_ERROR, "BsdDetour::Open(): error: unable to open ProtoRouteMgr\n");
408 delete rtMgr;
409 Close();
410 return false;
411 }
412 unsigned int ifIndexArray[256];
413 unsigned int ifCount = ProtoSocket::GetInterfaceIndices(ifIndexArray, 256);
414 if (0 == ifCount)
415 {
416 PLOG(PL_ERROR, "BsdDetour::Open(): warning: no network interface indices were found.\n");
417 }
418 else if (ifCount > 256)
419 {
420 PLOG(PL_ERROR, "BsdDetour::Open(): warning: found network interfaces indices exceeding maximum count.\n");
421 ifCount = 256;
422 }
423 // Add any IP addrs assigned to this iface to our if_addr_list
424 for (unsigned int i = 0; i < ifCount; i++)
425 {
426 unsigned int ifIndex = ifIndexArray[i];
427 ProtoAddressList tempList;
428 if (!rtMgr->GetInterfaceAddressList(ifIndex, ProtoAddress::IPv4, tempList))
429 {
430 PLOG(PL_ERROR, "BsdDetour::Open() warning: couldn't retrieve IPv4 address for iface index: %d\n", ifIndex);
431 }
432 if (!rtMgr->GetInterfaceAddressList(ifIndex, ProtoAddress::IPv6, tempList))
433 {
434 PLOG(PL_ERROR, "BsdDetour::Open() warning: couldn't retrieve IPv6 address for iface index: %d\n", ifIndex);
435 }
436 ProtoAddressList::Iterator it(tempList);
437 ProtoAddress addr;
438 while (it.GetNextAddress(addr))
439 {
440 AddressListItem* ifItem = new AddressListItem(addr, ifIndex);
441 if (NULL == ifItem)
442 {
443 PLOG(PL_ERROR, "BsdDetour::Open() new AddressListItem error: %s\n", GetErrorString());
444 delete rtMgr;
445 Close();
446 return false;
447 }
448 if_addr_list.Insert(*ifItem);
449 }
450 }
451 delete rtMgr;
452 return true;
453 } // end BsdDetour::Open()
454
SetMulticastInterface(const char * interfaceName)455 bool BsdDetour::SetMulticastInterface(const char* interfaceName)
456 {
457 ASSERT(IsOpen());
458 if (NULL != interfaceName)
459 {
460 int result;
461 #ifdef HAVE_IPV6
462 if (AF_INET6 == domain)
463 {
464 unsigned int interfaceIndex = ProtoSocket::GetInterfaceIndex(interfaceName);
465 result = setsockopt(descriptor, IPPROTO_IPV6, IPV6_MULTICAST_IF,
466 (char*)&interfaceIndex, sizeof(interfaceIndex));
467 }
468 else
469 #endif // HAVE_IPV6
470 {
471 struct in_addr localAddr;
472 ProtoAddress interfaceAddress;
473 if (ProtoSocket::GetInterfaceAddress(interfaceName, ProtoAddress::IPv4, interfaceAddress))
474 {
475 localAddr.s_addr = htonl(interfaceAddress.IPv4GetAddress());
476 }
477 else
478 {
479 PLOG(PL_ERROR, "BsdDetour::SetMulticastInterface() invalid interface name\n");
480 return false;
481 }
482 result = setsockopt(descriptor, IPPROTO_IP, IP_MULTICAST_IF, (char*)&localAddr,
483 sizeof(localAddr));
484 }
485 if (result < 0)
486 {
487 PLOG(PL_ERROR, "BsdDetour: setsockopt(IP_MULTICAST_IF) error: %s\n",
488 GetErrorString());
489 return false;
490 }
491 }
492 return true;
493 } // end BsdDetour::SetMulticastInterface()
494
Close()495 void BsdDetour::Close()
496 {
497 if (0 != hook_flags)
498 {
499 for (unsigned int i =0; i < rule_count; i++)
500 SetIPFirewall(DELETE, (int)rule_number[i],
501 src_filter_addr, src_filter_mask,
502 dst_filter_addr, dst_filter_mask);
503 rule_count = 0;
504 hook_flags = 0;
505 }
506 if (descriptor >= 0)
507 {
508 ProtoDetour::Close();
509 close(descriptor);
510 descriptor = INVALID_HANDLE;
511 }
512 } // end BsdDetour::Close()
513
Recv(char * buffer,unsigned int & numBytes,Direction * direction,ProtoAddress * srcMac,unsigned int * ifIndex)514 bool BsdDetour::Recv(char* buffer,
515 unsigned int& numBytes,
516 Direction* direction,
517 ProtoAddress* srcMac,
518 unsigned int* ifIndex)
519 {
520 // (TBD) can BSD divert socket get src MAC addr?
521 // (It looks like we _could_ get a full MAC header if we
522 // use a "layer 2" hook to capture packets in our
523 // ipfw rule)
524 if (NULL != srcMac) srcMac->Invalidate();
525
526 struct sockaddr_storage sockAddr;
527 socklen_t addrLen = sizeof(sockAddr);
528 ssize_t result = recvfrom(descriptor, buffer, numBytes, 0,
529 (struct sockaddr*)&sockAddr, &addrLen);
530 if (result < 0)
531 {
532 pkt_addr.Invalidate();
533 switch (errno)
534 {
535 case EINTR:
536 case EAGAIN:
537 numBytes = 0;
538 if (NULL != direction)
539 *direction = UNSPECIFIED;
540 return true;
541 default:
542 numBytes = 0;
543 PLOG(PL_ERROR, "BsdDetour::Recv() recvfrom() error: %s\n", GetErrorString());
544 return false;
545 }
546 }
547 else
548 {
549 pkt_addr.SetSockAddr(*((struct sockaddr*)&sockAddr));
550 numBytes = result;
551 if (NULL != direction)
552 {
553 if (pkt_addr.IsUnspecified())
554 *direction = OUTBOUND;
555 else
556 *direction = INBOUND;
557 }
558 if (NULL != ifIndex)
559 {
560 if (pkt_addr.IsUnspecified())
561 {
562 *ifIndex = 0;
563 }
564 else
565 {
566 // Lookup "ifIndex" for given interface IP address
567 AddressListItem* ifItem =
568 static_cast<AddressListItem*>(if_addr_list.Find(pkt_addr.GetRawHostAddress(), pkt_addr.GetLength() << 3));
569 if (NULL != ifItem)
570 *ifIndex = ifItem->GetInterfaceIndex();
571 else
572 *ifIndex = 0;
573 }
574 }
575 return true;
576 }
577 } // end BsdDetour::Recv()
578
Allow(const char * buffer,unsigned int numBytes)579 bool BsdDetour::Allow(const char* buffer, unsigned int numBytes)
580 {
581 if (pkt_addr.IsValid())
582 {
583 socklen_t addrSize = (ProtoAddress::IPv6 == pkt_addr.GetType()) ?
584 sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in);
585 ssize_t result = sendto(descriptor, buffer, (size_t)numBytes, 0,
586 &pkt_addr.GetSockAddr(), addrSize);
587 if (result < 0)
588 {
589 PLOG(PL_ERROR, "BsdDetour::Allow() sendto() error: %s\n", GetErrorString());
590 return false;
591 }
592 pkt_addr.Invalidate();
593 return true;
594 }
595 else
596 {
597 PLOG(PL_ERROR, "BsdDetour::Allow() error: no packet pending\n");
598 return false;
599 }
600 } // end BsdDetour::Allow()
601
Drop()602 bool BsdDetour::Drop()
603 {
604 if (pkt_addr.IsValid())
605 {
606 pkt_addr.Invalidate();
607 return true;
608 }
609 else
610 {
611 PLOG(PL_ERROR, "BsdDetour::Drop() error: no packet pending\n");
612 return false;
613 }
614 } // end BsdDetour::Drop()
615
Inject(const char * buffer,unsigned int numBytes)616 bool BsdDetour::Inject(const char* buffer, unsigned int numBytes)
617 {
618 if (inject_only)
619 {
620 unsigned char version = buffer[0];
621 version >>= 4;
622 ProtoAddress dstAddr;
623 socklen_t addrLen;
624 if (4 == version)
625 {
626 dstAddr.SetRawHostAddress(ProtoAddress::IPv4, buffer+16, 4);
627 addrLen = sizeof(struct sockaddr);
628 }
629 else if (6 == version)
630 {
631 //PLOG(PL_ERROR, "BsdDetour::Inject() IPv6 injection not yet supported!\n");
632 dstAddr.SetRawHostAddress(ProtoAddress::IPv6, buffer+24, 16);
633 addrLen = sizeof(struct sockaddr_in6);
634 //return false;
635 }
636 else
637 {
638 PLOG(PL_ERROR, "BsdDetour::Inject() unknown IP version!\n");
639 return false;
640 }
641 size_t result = sendto(descriptor, buffer, numBytes, 0,
642 &dstAddr.GetSockAddr(), addrLen);
643 if (result != numBytes)
644 {
645 PLOG(PL_ERROR, "BsdDetour::Inject() sendto() error: %s\n", GetErrorString());
646 return false;
647 }
648 return true;
649 }
650 else
651 {
652 size_t result = write(descriptor, buffer, (size_t)numBytes);
653 if (result != numBytes)
654 {
655 PLOG(PL_ERROR, "BsdDetour::Inject() write() error: %s\n", GetErrorString());
656 return false;
657 }
658 }
659 return true;
660 } // end BsdDetour::Inject()
661
662