📄 divertsocket.cc
字号:
/* * fromdevice.{cc,hh} -- element diverts IP packets into Click using divert sockets * Alexander Yip * * Copyright (c) 2001 Massachusetts Institute of Technology * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, subject to the conditions * listed in the Click LICENSE file. These conditions include: you must * preserve this copyright notice, and you cannot mention the copyright * holders in advertising related to the Software without their permission. * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This * notice is a summary of the Click LICENSE file; the license in that file is * legally binding. */#include <click/config.h>#include "divertsocket.hh"#include <click/error.hh>#include <click/confparse.hh>#include <click/glue.hh>#include <unistd.h>#include <fcntl.h># include <net/if.h>#if defined(__FreeBSD__ )# include <sys/ioctl.h># include <sys/socket.h># include <sys/types.h>#elif defined(__linux__)# include <features.h># include <net/if_packet.h># include <netinet/ip.h># include <netinet/in.h># include <netinet/tcp.h># include <netinet/udp.h># include <sys/param.h># include <linux/types.h># include <linux/icmp.h># if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1# include <netpacket/packet.h># include <net/ethernet.h># else# include <linux/if_packet.h># include <linux/if_ether.h># endif#endif#include <clicknet/ip.h>DivertSocket::DivertSocket(){ _fd = -1;}DivertSocket::~DivertSocket() { uninitialize();}voidDivertSocket::notify_ninputs(int n){ set_ninputs(n);}voidDivertSocket::notify_noutputs(int n){ set_noutputs(n);}int DivertSocket::parse_ports(const String ¶m, ErrorHandler *errh, int32_t *portl, int32_t *porth) { int dash; *portl = *porth = 0;; dash = param.find_left('-'); if (dash < 0) dash = param.length(); if (!cp_integer(param.substring(0,dash), portl)){ //errh->error("1 bad port in rule spec"); return -1; } if (dash < param.length()) { if (!cp_integer(param.substring(dash+1), porth)) { //errh->error("2 bad port in rule spec"); return -1; } } else *porth = *portl; if (*portl > *porth || *portl < 0 || *porth > 0xFFFF) { errh->error("port(s) %d-%d out of range in rule spec", portl, porth); return -1; } return 0;}intDivertSocket::configure(const Vector<String> &conf, ErrorHandler *errh){ int confindex = 5; _have_sport = _have_dport = false; _setup_fw = true;#ifdef 0 for(int i=0; i < conf.size(); i++){ click_chatter(" %s\n", ((String)conf[i]).cc()); }#endif if (conf.size() == 1) { if (cp_va_parse(conf[0], this, errh, cpUnsigned, "divertport", &_divertport, cpEnd) < 0) return -1; _setup_fw = false; return 0; } if (conf.size() < confindex+1) { errh->error("not enough parameters for DivertSocket"); return -1; } if (conf.size() > confindex+4) { errh->error("too many parameters for DivertSocket"); return -1; } /* click_chatter("Hello 1 this = %x\n", (void *)this); click_chatter("Hello 2 this = %x\n", (void *)this); click_chatter("conf[0] = %s\n", conf[0].cc()); */ // parse devicename if (cp_va_parse(conf[0], this, errh, cpString, "device", &_device, cpEnd) < 0) return -1; // parse divert port number if (cp_va_parse(conf[1], this, errh, cpUnsigned, "divertport", &_divertport, cpEnd) < 0) return -1; // parse rule number if (cp_va_parse(conf[2], this, errh, cpUnsigned, "rulenumber", &_rulenumber, cpEnd) < 0) return -1; // parse protocol & src addr/mask if ((cp_va_parse(conf[3], this, errh, cpByte, "protocol", &_protocol, cpEnd) < 0) || (!cp_ip_prefix(conf[4], &_saddr, &_smask, true, this))) { errh->error("invalid src addr/mask"); return -1; } _saddr &= _smask; if ((_protocol != IP_PROTO_UDP && _protocol != IP_PROTO_TCP) && (conf.size() > 7)) { errh->error("too many parameters for non TCP/UDP rule"); return -1; } // parse src ports if (_protocol == IP_PROTO_UDP || _protocol == IP_PROTO_TCP) { if (parse_ports(conf[5], errh, &_sportl, &_sporth) < 0) _have_sport = false; else { _have_sport = true; confindex++; } } else if (parse_ports(conf[5], errh, &_sportl, &_sporth) >= 0) { errh->error("ports not required for non TCP/UDP rules"); return -1; } //printf("1 confindex = %d (%s)\n", confindex, conf[confindex].cc()); // parse dst addr/mask if (!cp_ip_prefix(conf[confindex], &_daddr, &_dmask, true, this )){ errh->error("invalid dst addr/mask"); return -1; } _daddr &= _dmask; confindex++; // parse dst ports if (confindex < conf.size()) { //printf("2 confindex = %d (%s)\n", confindex, conf[confindex].cc()); if (_protocol == IP_PROTO_UDP || _protocol == IP_PROTO_TCP) { if (parse_ports(conf[confindex], errh, &_dportl, &_dporth) < 0) _have_dport = false; else { _have_dport = true; confindex++; } } else if (parse_ports(conf[confindex], errh, &_dportl, &_dporth) >= 0) { errh->error("ports not required for non TCP/UDP rules"); return -1; } } // parse in/out if (confindex < conf.size() ) { if (cp_va_parse(conf[confindex], this, errh, cpString, "in/out", &_inout, cpEnd) < 0) return -1; if ( (_inout != "") && (_inout != "in") && (_inout != "out") ) { errh->error("illegal direction specifier: '%s'", _inout.cc()); return -1; } } return 0;}intDivertSocket::setup_firewall(ErrorHandler *errh) {#if defined(__FreeBSD__) char tmp[512]; char sport[32], dport[32], prot[8];#elif defined(__linux__) char *fw_policy="DIVERT"; char fw_chain[32];#endif // Setup firewall to divert sockets#if defined(__FreeBSD__) if (_protocol == 0) sprintf(prot, "ip"); else sprintf(prot, "%d", _protocol); if (_have_sport) { if (_sportl == _sporth) sprintf(sport, "%d", _sportl); else sprintf(sport, "%d-%d", _sportl, _sporth); } else { sport[0]=0; } if (_have_dport) { if (_dportl == _dporth) sprintf(dport, "%d", _dportl); else sprintf(dport, "%d-%d", _dportl, _dporth); } else { dport[0]=0; } sprintf(tmp, "/sbin/ipfw add %u divert %u %s from %s:%s %s to %s:%s %s %s via %s", _rulenumber, _divertport, prot, _saddr.s().cc(), _smask.s().cc(), sport, _daddr.s().cc(), _dmask.s().cc(), dport, _inout.cc(), _device.cc() ); printf("%s\n", tmp); if (system(tmp) != 0) { close (_fd); errh->error("ipfw failed"); return -1; }#elif defined(__linux__) /* fill in the rule first */ bzero(&fw, sizeof (struct ip_fw)); fw.fw_proto= _protocol; fw.fw_redirpt=htons(bindPort.sin_port); //fw.fw_redirpt=bindPort.sin_port; if (_have_sport) { fw.fw_spts[0]=_sportl; fw.fw_spts[1]=_sporth; } else { fw.fw_spts[0]=0; fw.fw_spts[1]=0xffff; } if (_have_dport) { fw.fw_dpts[0]=_dportl; fw.fw_dpts[1]=_dporth; } else { fw.fw_dpts[0]=0; fw.fw_dpts[1]=0xffff; } fw.fw_src.s_addr = _saddr.in_addr().s_addr; fw.fw_smsk.s_addr= _smask.in_addr().s_addr; fw.fw_dst.s_addr = _daddr.in_addr().s_addr; fw.fw_dmsk.s_addr= _dmask.in_addr().s_addr; fw.fw_outputsize=0xffff; strcpy(fw.fw_vianame, _device.cc() ); /* fill in the fwuser structure */ ipfu.ipfw=fw; memcpy(ipfu.label, fw_policy, strlen(fw_policy)); /* fill in the fwnew structure */ ipfc.fwn_rule=ipfu; ipfc.fwn_rulenum = _rulenumber; /* open a socket */ if ((fw_sock=socket(AF_INET, SOCK_RAW, IPPROTO_RAW))==-1) { errh->error("could not create raw socket for firewall setup"); return -1; } if (_inout == "in") { strcpy(fw_chain, "input"); memcpy(ipfc.fwn_label, fw_chain, strlen(fw_chain)); } else if (_inout == "out") { strcpy(fw_chain, "output"); memcpy(ipfc.fwn_label, fw_chain, strlen(fw_chain)); } else { memcpy(&ipfc2, &ipfc, sizeof(ipfc)); strcpy(fw_chain, "input"); memcpy(ipfc.fwn_label, fw_chain, strlen(fw_chain)); strcpy(fw_chain, "output"); memcpy(ipfc2.fwn_label, fw_chain, strlen(fw_chain)); /* write a rule into it */ if (setsockopt(fw_sock, IPPROTO_IP, IP_FW_INSERT, &ipfc2, sizeof(ipfc2))==-1) { errh->error("could not set output firewall rule: %s", strerror(errno)); return -1; } } /* write a rule into it */ if (setsockopt(fw_sock, IPPROTO_IP, IP_FW_INSERT, &ipfc, sizeof(ipfc))==-1) { errh->error("could not set firewall rule: %s",strerror(errno)); return -1; }#else close(_fd); errh->error("This platform is not yet supported by DivertSocket"); return -1; #endif return 0;} intDivertSocket::initialize(ErrorHandler *errh) { int ret, n; struct sockaddr_in bindPort; //, sin;#ifdef 0 printf("Device : \t%s\n", _device.cc()); printf("DIV port: \t%u\n", _divertport); printf("Rule Num: \t%u\n", _rulenumber); printf("Protocol: \t%d\n", _protocol); printf("src/mask: \t%s / %s\n", _saddr.s().cc(), _smask.s().cc()); printf("dst/mask: \t%s / %s\n", _daddr.s().cc(), _dmask.s().cc()); printf("sport : \t%u - %u\n", _sportl, _sporth); printf("dport : \t%u - %u\n", _dportl, _dporth); printf("in/out : \t%s\n", _inout.cc());#endif // Setup divert socket _fd = socket(AF_INET, SOCK_RAW, IPPROTO_DIVERT); if (_fd == -1) { errh->error("DivertSocket(socket): %s", strerror(errno)); return -1; } bindPort.sin_family=AF_INET; bindPort.sin_port=htons(_divertport); bindPort.sin_addr.s_addr=0; // set REUSE option n = 1; if (setsockopt (_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&n, sizeof (n)) < 0) { errh->error("could not set REUSEADDR"); close (_fd); return -1; } // bind to port ret=bind(_fd, (struct sockaddr *)&bindPort, sizeof(struct sockaddr_in)); if (ret != 0) { close(_fd); errh->error("DivertSocket(bind): %s", strerror(errno)); return -1; } fcntl(_fd, F_SETFL, O_NONBLOCK); // setup firewall if (_setup_fw && (ret = setup_firewall(errh) < 0)) return ret; add_select(_fd, SELECT_READ); return 0;} void DivertSocket::uninitialize(){ if (_fd >= 0) { if (_setup_fw) {#if defined(__FreeBSD__) char tmp[64]; sprintf(tmp, "/sbin/ipfw delete %u", _rulenumber); system(tmp); #elif defined(__linux__) struct ip_fwdelnum ipfwd; ipfwd.fwd_rulenum = ipfc.fwn_rulenum; strcpy(ipfwd.fwd_label, ipfc.fwn_label); if (setsockopt(fw_sock, IPPROTO_IP, IP_FW_DELETE_NUM, &ipfwd, sizeof(ipfwd))==-1) { fprintf(stderr, "could not remove firewall rule"); } if (_inout == "") { ipfwd.fwd_rulenum = ipfc2.fwn_rulenum; strcpy(ipfwd.fwd_label, ipfc2.fwn_label); if (setsockopt(fw_sock, IPPROTO_IP, IP_FW_DELETE_NUM, &ipfwd, sizeof(ipfwd))==-1) { fprintf(stderr, "could not remove output firewall rule"); } } close(fw_sock); #else #endif } //fprintf(stderr, "closing _fd\n"); close (_fd); remove_select(_fd, SELECT_READ); _fd = -1; }}voidDivertSocket::selected(int fd) { struct sockaddr_in sa; socklen_t fromlen; WritablePacket *p; int len; if (fd != _fd) return; fromlen = sizeof(sa); p = Packet::make(2, 0, 2046, 0); // YIPAL bufsize len = recvfrom(_fd, p->data(), p->length(), 0, (sockaddr *)&sa, &fromlen); if (len > 0) { // set the timestamp p->timestamp_anno() = Timestamp::now(); p->change_headroom_and_length(2, len); output(0).push(p); } else { p->kill(); if (len <= 0 && errno != EAGAIN) click_chatter("DivertSocket: recvfrom: %s", strerror(errno)); }}voidDivertSocket::send_packet(Packet *p){ int n; struct sockaddr_in sa; sa.sin_len = sizeof(sa); sa.sin_family = AF_INET; sa.sin_port = htons(_divertport); memcpy(&sa.sin_addr.s_addr, p->data() + 16, 4); //printf("address: 0x%x\n", sa.sin_addr.s_addr); n = sendto(_fd, p->data(), p->length() , 0, (sockaddr *)&sa, sizeof(sa)); //click_chatter(" %i bytes reinjected.", n); if (n < 0) click_chatter("DivertSocket: %s", strerror(errno)); p->kill();}voidDivertSocket::push(int, Packet *p){ assert(p->length() >= 20); send_packet(p);}ELEMENT_REQUIRES(userlevel)EXPORT_ELEMENT(DivertSocket)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -