/* dect source plugin * (c) 2008 by Mike Kershaw #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //#include "audioDecode.h" #define MODE_ASYNC_FP_SCAN 0 #define MODE_ASYNC_PP_SCAN 1 #define MODE_SYNC_CALL_SCAN 2 #define COA_IOCTL_MODE 0xD000 #define COA_IOCTL_CHAN 0xD004 #define COA_IOCTL_SETRFPI 0xD008 #define COA_MODE_SNIFF 0x0300 #define COA_SUBMODE_SNIFF_ALL 0x0000 #define COA_SUBMODE_SNIFF_SCANFP 0x0001 #define COA_SUBMODE_SNIFF_SCANPP 0x0002 #define COA_SUBMODE_SNIFF_SYNC 0x0003 /* DECT Client Command Defs */ #define DECT_CMD_START 0 #define DECT_CMD_END 9 #define DECT_SUBCMD_START 0 #define DECT_SUBCMD_END 9 /* Commands */ #define DECT_CMD_CHANHOP 0 #define DECT_CMD_SCAN 1 /* Subcommands */ #define DECT_SUBCMD_CHANHOP_ENABLE 1 #define DECT_SUBCMD_CHANHOP_DISABLE 0 #define DECT_SUBCMD_SCAN_FP 0 #define DECT_SUBCMD_SCAN_PP 1 #define DECT_SUBCMD_SCAN_CALLS 2 // Globals int dect_comp_datachunk; static int mode = MODE_ASYNC_FP_SCAN; static int switched = 0; /* This is the 7 bytes we read while scanning */ typedef struct { uint8_t channel; uint8_t RSSI; uint8_t RFPI[5]; } dect_data_scan_t; typedef struct { dect_data_scan_t sdata; uint32_t first_seen; uint32_t last_seen; uint32_t count_seen; } dect_data_t; typedef struct { unsigned char rssi; unsigned char channel; unsigned char slot; struct timespec timestamp; unsigned char data[53]; } pp_packet_t; // Define an enum of all the fields we send enum DECT_fields { DECT_RFPI, DECT_RSSI, DECT_channel, DECT_first_seen, DECT_last_seen, DECT_count_seen, DECT_maxfield }; // Define the names of each const char *DECT_fields_text[] = { "rfpi", "rssi", "channel", "first_seen", "last_seen", "count_seen", NULL }; // Prototypes int dect_register(GlobalRegistry *); int dect_unregister(GlobalRegistry *); // Prototype the network write function int Protocol_DECT(PROTO_PARMS); void Protocol_DECT_enable(PROTO_ENABLE_PARMS); // Timer prototype for kicking DECT network data at regular intervals int dect_timer(TIMEEVENT_PARMS); // Prototype callback hook for attaching to the chain int dect_chain_hook(CHAINCALL_PARMS); // DECT data in a packet class dect_datachunk : public packet_component { public: dect_data_scan_t sdata; pp_packet_t pdata; int kind; bool sync; }; class Dumpfile_Dectpcap : public Dumpfile { public: Dumpfile_Dectpcap(); Dumpfile_Dectpcap(GlobalRegistry *in_globalreg); virtual ~Dumpfile_Dectpcap(); virtual int chain_handler(kis_packet *in_pack); virtual int Flush(); private: FILE *dectpcapfile; pcap_t *pcap; pcap_dumper_t *pcap_d; }; // Dumpfile stuff int dumpfiledectpcap_chain_hook(CHAINCALL_PARMS) { Dumpfile_Dectpcap *auxptr = (Dumpfile_Dectpcap *) auxdata; return auxptr->chain_handler(in_pack); } Dumpfile_Dectpcap::Dumpfile_Dectpcap() { _MSG("FATAL OOPS: Dumpfile_Dectpcap called with no globalreg", MSGFLAG_ERROR); exit(1); } Dumpfile_Dectpcap::Dumpfile_Dectpcap(GlobalRegistry *in_globalreg) : Dumpfile(in_globalreg) { char errstr[STATUS_MAX]; globalreg = in_globalreg; dectpcapfile = NULL; type = "dectpcap"; if (globalreg->packetchain == NULL) { _MSG("FATAL OOPS: Packetchain missing before Dumpfile_Dectpcap", MSGFLAG_ERROR); exit(1); } // Find the file name if ((fname = ProcessConfigOpt("dectpcap")) == "" || globalreg->fatal_condition) { return; } dectpcapfile = fopen(fname.c_str(), "w"); if (dectpcapfile == NULL) { snprintf(errstr, STATUS_MAX, "Failed to open dectpcap dump file '%s': %s", fname.c_str(), strerror(errno)); globalreg->fatal_condition = 1; return; } _MSG("Dumping to " + fname, MSGFLAG_INFO); pcap = pcap_open_dead(DLT_EN10MB, 73); if (!pcap) { _MSG("couldn't pcap_open_dead(" + fname + ")", MSGFLAG_ERROR); } pcap_d = pcap_dump_open(pcap, fname.c_str()); if (!pcap_d) { _MSG("couldn't pcap_dump_open(" + fname + ")", MSGFLAG_ERROR); } globalreg->packetchain->RegisterHandler(&dumpfiledectpcap_chain_hook, this, CHAINPOS_LOGGING, -100); globalreg->RegisterDumpFile(this); } Dumpfile_Dectpcap::~Dumpfile_Dectpcap() { int opened = 0; globalreg->packetchain->RemoveHandler(&dumpfiledectpcap_chain_hook, CHAINPOS_LOGGING); // Close files if (dectpcapfile != NULL) { Flush(); fclose(dectpcapfile); opened = 1; } dectpcapfile = NULL; } int Dumpfile_Dectpcap::Flush() { if (dectpcapfile == NULL) return 0; fflush(dectpcapfile); return 1; } int Dumpfile_Dectpcap::chain_handler(kis_packet *in_pack) { if (dectpcapfile == NULL) return 0; if (in_pack->error) return 0; dect_datachunk *dc = (dect_datachunk *) in_pack->fetch(dect_comp_datachunk); // We only want kind MODE_SYNC_CALL_SCAN if (!dc || dc->kind != MODE_SYNC_CALL_SCAN) { return 0; } struct pcap_pkthdr pcap_hdr; pcap_hdr.caplen = 73; pcap_hdr.len = 73; int ret = gettimeofday(&pcap_hdr.ts, NULL); if (ret) { _MSG("couldn't gettimeofday(): " + string(strerror(errno)), MSGFLAG_ERROR); return 0; } uint8_t pcap_packet[100]; memset(pcap_packet, 0, 100); pcap_packet[12] = 0x23; pcap_packet[13] = 0x23; pcap_packet[14] = 0x00; /* decttype (receive) */ pcap_packet[15] = dc->pdata.channel; pcap_packet[16] = 0; pcap_packet[17] = dc->pdata.slot; pcap_packet[18] = 0; pcap_packet[19] = dc->pdata.rssi; memcpy(&pcap_packet[20], dc->pdata.data, 53); pcap_dump((u_char*)pcap_d, &pcap_hdr, pcap_packet); dumped_frames++; return 1; } // Packetsource - where packets come from. Reads the device and writes into the chain class PacketSource_Dect : public KisPacketSource { public: PacketSource_Dect() { _MSG("FATAL OOPS: KisDectSource()", MSGFLAG_ERROR); exit(1); } PacketSource_Dect(GlobalRegistry *in_globalreg) : KisPacketSource(in_globalreg), locked(false), sync(false), globalreg(in_globalreg) { } PacketSource_Dect(GlobalRegistry *in_globalreg, string in_interface, vector *in_opts) : KisPacketSource(in_globalreg, in_interface, in_opts), locked(false), sync(false), globalreg(in_globalreg) { serial_fd = -1; ParseOptions(in_opts); } virtual ~PacketSource_Dect() { if (serial_fd >= 0) { close(serial_fd); } } virtual void SetExternal(PacketSource_Dect *ex) { external = ex; } virtual PacketSource_Dect* GetExternal(void) { return external; } virtual PacketSource_Dect *CreateSource(GlobalRegistry *in_globalreg, string interface, vector *in_opts) { PacketSource_Dect *ref = new PacketSource_Dect(in_globalreg, interface, in_opts); // XXX Hackalert SetExternal(ref); return ref; } virtual int AutotypeProbe(string in_device) { if (StrLower(in_device) == "dect") { type = "dect"; return 1; } return 0; } // No low-level radio data to fetch virtual void FetchRadioData(kis_packet *in_pack) { return; } virtual int RegisterSources(Packetsourcetracker *tracker) { // source type dect, channel list "DECT", not root tracker->RegisterPacketProto("dect", this, "DECT", 0); return 1; } virtual int ParseOptions(vector *in_opts) { if ((serialdevice = FetchOpt("device", in_opts)) == "") { _MSG("Packetsource::Dect expected option 'device' for serial dev", MSGFLAG_PRINTERROR); return -1; } } virtual int FetchChannelCapable() { return 1; } virtual int EnableMonitor() { // Do setup, but remember this is called before OpenSource() return 1; } virtual int DisableMonitor() { // Same return 1; } virtual int SetChannel(unsigned int in_ch) { if (locked) { return 0; } stringstream c; c << in_ch; _MSG("switching to channel " + c.str(), MSGFLAG_INFO); if (ioctl(serial_fd, COA_IOCTL_CHAN, &in_ch)){ _MSG("couldn't ioctl()", MSGFLAG_ERROR); return 0; } return 1; } void startScanFp() { if (serial_fd == -1) return; /* start sniffer mode */ int val = COA_MODE_SNIFF|COA_SUBMODE_SNIFF_SCANFP; if (ioctl(serial_fd, COA_IOCTL_MODE, &val)) { _MSG("Couldn't ioctl to COA_MODE_SNIFF", MSGFLAG_ERROR); return; } mode = MODE_ASYNC_FP_SCAN; switched = 1; if (sync) { _MSG("Sync off.", MSGFLAG_INFO); sync = false; } // Remove lock, if there is any, and start scanning at channel 0 setLock(false, 0); } void startScanPp() { if (serial_fd == -1) return; /* start sniffer mode */ int val = COA_MODE_SNIFF | COA_SUBMODE_SNIFF_SCANPP; if (ioctl(serial_fd, COA_IOCTL_MODE, &val)) { _MSG("Couldn't ioctl to COA_MODE_SNIFF", MSGFLAG_ERROR); return; } mode = MODE_ASYNC_PP_SCAN; switched = 1; if (sync) { _MSG("Sync off.", MSGFLAG_INFO); sync = false; } // Remove lock, if there is any, and start scanning at channel 0 setLock(false, 0); } void startScanCalls(uint8_t *RFPI, int channel) { if (serial_fd == -1 || !RFPI) return; /* set sync sniff mode */ uint16_t val; val = COA_MODE_SNIFF | COA_SUBMODE_SNIFF_SYNC; if (ioctl(serial_fd, COA_IOCTL_MODE, &val)){ _MSG("Couldn't ioctl to COA_MODE_SNIFF", MSGFLAG_ERROR); return; } /* set rfpi to sync with */ if(ioctl(serial_fd, COA_IOCTL_SETRFPI, RFPI)){ _MSG("Couldn't ioctl SETRFPI", MSGFLAG_ERROR); return; } mode = MODE_SYNC_CALL_SCAN; if (sync) { sync = false; } // Don't hop channels while synced setLock(true, channel); } virtual int FetchDescriptor() { // If -1 this will be handled upstream return serial_fd; } virtual int OpenSource() { short val; if((serial_fd = open(serialdevice.c_str(), O_RDONLY)) == -1) { _MSG("Could not open " + serialdevice, MSGFLAG_ERROR); return 0; } startScanFp(); return 1; } virtual int CloseSource() { // Issue any shutdown you need to do if (serial_fd >= 0) close(serial_fd); serial_fd = -1; return 1; } virtual int Poll() { unsigned char buf[7]; int rbytes = -1; kis_packet *newpack = globalreg->packetchain->GeneratePacket(); dect_datachunk *dc = new dect_datachunk; // Async FP/PP scan read 7 bytes each if (mode == MODE_ASYNC_FP_SCAN || mode == MODE_ASYNC_PP_SCAN) { if ((rbytes = read(serial_fd, &(dc->sdata), 7)) != 7) { // Fail stringstream s; s << rbytes; _MSG("Bad read. Expected: 7 Got: " + s.str(), MSGFLAG_ERROR); return 0; } else { char station[32]; sprintf(station, "%.2x:%.2x:%.2x:%.2x:%.2x", dc->sdata.RFPI[0], dc->sdata.RFPI[1], dc->sdata.RFPI[2], dc->sdata.RFPI[3], dc->sdata.RFPI[4]); _MSG("RFPI: " + string(station), MSGFLAG_INFO); dc->kind = mode; newpack->insert(dect_comp_datachunk, dc); globalreg->packetchain->ProcessPacket(newpack); } } else if (mode == MODE_SYNC_CALL_SCAN) { if ((rbytes = read(serial_fd, &(dc->pdata), sizeof(dc->pdata))) != sizeof(dc->pdata)) { stringstream s, s2; s << (sizeof(dc->pdata)); s2 << rbytes; _MSG("Bad read. Expected: " + s.str() + " Got: " + s2.str(), MSGFLAG_ERROR); return 0; } else { if (!sync) { _MSG("Got sync.", MSGFLAG_INFO); sync = true; } dc->sync = sync; dc->kind = MODE_SYNC_CALL_SCAN; newpack->insert(dect_comp_datachunk, dc); globalreg->packetchain->ProcessPacket(newpack); } } else { _MSG("Bad mode selected", MSGFLAG_ERROR); return 0; } return 1; } void setLock(bool lock, int arg) { if (arg != -1) { stringstream chan; chan << arg; _MSG("Locking to " + chan.str(), MSGFLAG_INFO); SetChannel(arg); } locked = lock; } protected: string serialdevice; int serial_fd; bool locked; bool sync; PacketSource_Dect *external; GlobalRegistry *globalreg; }; class DectTracker { public: DectTracker() { _MSG("FATAL OOPS: DectTracker()", MSGFLAG_ERROR); } DectTracker(GlobalRegistry *in_globalreg) { globalreg = in_globalreg; // Register a handler in the packet chain in the classifier category, // tho we're going to do our own decoding globalreg->packetchain->RegisterHandler(&dect_chain_hook, this, CHAINPOS_CLASSIFIER, -100); // Register the network protocol proto_ref = globalreg->kisnetserver->RegisterProtocol("DECT", 0, 1, DECT_fields_text, &Protocol_DECT, &Protocol_DECT_enable, this); // Add a timer for once a second to send updated DECT data time_ref = globalreg->timetracker->RegisterTimer(SERVER_TIMESLICES_SEC, NULL, 1, &dect_timer, this); } // Send dect records void BlitDECTProto(int in_fd) { for (map::iterator x = dect_map.begin(); x != dect_map.end(); ++x) { kis_protocol_cache cache; if (in_fd == -1) { if (globalreg->kisnetserver->SendToAll(proto_ref, (void *) x->second) < 0) break; } else { if (globalreg->kisnetserver->SendToClient(in_fd, proto_ref, (void *) x->second, &cache) < 0) break; } } } int chain_handler(kis_packet *in_pack) { dect_datachunk *dectinfo = (dect_datachunk *) in_pack->fetch(dect_comp_datachunk); int currentmode = mode; // If this packet doesn't have dect info or if it's call data, move // along. if (dectinfo == NULL || dectinfo->kind == MODE_SYNC_CALL_SCAN) { return 0; } if (switched) { dect_map.clear(); switched = 0; } dect_data_t *td = new dect_data_t; memcpy(td->sdata.RFPI, &(dectinfo->sdata.RFPI), 5); td->sdata.channel = dectinfo->sdata.channel; td->sdata.RSSI = dectinfo->sdata.RSSI; td->first_seen = time(NULL); td->last_seen = time(NULL); td->count_seen = 1; bool found = false; map::iterator x = dect_map.begin(); for (; x != dect_map.end(); ++x) { if (!memcmp(x->second->sdata.RFPI, td->sdata.RFPI, 5)) { found = true; // Update x->second->sdata.RSSI = td->sdata.RSSI; x->second->last_seen = time(NULL); x->second->count_seen++; if (x->second->sdata.channel != td->sdata.channel) { char station[32]; sprintf(station, "Station %.2x:%.2x:%.2x:%.2x:%.2x\n", td->sdata.RFPI[0], td->sdata.RFPI[1], td->sdata.RFPI[2], td->sdata.RFPI[3], td->sdata.RFPI[4]); stringstream s, c; s << td->sdata.channel; c << x->second->sdata.channel; _MSG(string(station) + " changed channels from " + s.str() + " to " + c.str(), MSGFLAG_INFO); x->second->sdata.channel = td->sdata.channel; } } } if (!found) { dect_map[x->first + 1] = td; } return 1; } void emptyMap(void) { this->dect_map.clear(); } protected: GlobalRegistry *globalreg; map dect_map; int time_ref; int proto_ref; // What we track about something dect_data_t ddata; }; // This really should be a struct class DectCcc { public: PacketSource_Dect *psd; DectTracker *dtracker; }; int dect_cc_callback(CLIENT_PARMS) { int cmd = -1; int subcmd = -1; int arg = -1; uint8_t rfpi[5]; char rfpi_s[15]; DectCcc *dc = (DectCcc *)auxptr; if (!dc) { _MSG("Bad arg.", MSGFLAG_ERROR); return 0; } PacketSource_Dect *psd = dc->psd; PacketSource_Dect *ex_psd = psd->GetExternal(); DectTracker *dtracker = dc->dtracker; if (!psd || !ex_psd || !dtracker) { _MSG("Bad args.", MSGFLAG_ERROR); return 0; } memset(rfpi, 0, sizeof(rfpi)); memset(rfpi_s, 0, sizeof(rfpi_s)); if (parsedcmdline->size() < 3) { _MSG("Bad client command.", MSGFLAG_ERROR); return 0; } _MSG("Got CMD " + (*parsedcmdline)[0].word + ", SUBCMD " + (*parsedcmdline)[1].word + ", ARG " + (*parsedcmdline)[2].word, MSGFLAG_INFO); cmd = atoi((*parsedcmdline)[0].word.c_str()); subcmd = atoi((*parsedcmdline)[1].word.c_str()); arg = atoi((*parsedcmdline)[2].word.c_str()); if (cmd < DECT_CMD_START || cmd > DECT_CMD_END || subcmd < DECT_SUBCMD_START || subcmd > DECT_SUBCMD_END) { _MSG("Bad DECT client command: " + cmdline, MSGFLAG_ERROR); return 0; } if (parsedcmdline->size() == 4) { // XXX Do this much more elegant long rfpi0, rfpi1, rfpi2, rfpi3, rfpi4; strncpy(rfpi_s, (*parsedcmdline)[3].word.c_str(), 14); sscanf(rfpi_s, "%x:%x:%x:%x:%x", &rfpi0, &rfpi1, &rfpi2, &rfpi3, &rfpi4); rfpi[0] = (uint8_t)rfpi0; rfpi[1] = (uint8_t)rfpi1; rfpi[2] = (uint8_t)rfpi2; rfpi[3] = (uint8_t)rfpi3; rfpi[4] = (uint8_t)rfpi4; } switch (cmd) { case DECT_CMD_CHANHOP: switch (subcmd) { case DECT_SUBCMD_CHANHOP_ENABLE: _MSG("DECT_CMD_CHANHOP ENABLE", MSGFLAG_INFO); psd->setLock(false, arg); break; case DECT_SUBCMD_CHANHOP_DISABLE: _MSG("DECT_CMD_CHANHOP DISABLE", MSGFLAG_INFO); psd->setLock(true, arg); break; default: _MSG("Bad DECT_CMD_CHANHOP subcommand.", MSGFLAG_ERROR); break; } break; case DECT_CMD_SCAN: switch(subcmd) { case DECT_SUBCMD_SCAN_FP: _MSG("DECT_CMD_SCAN FP", MSGFLAG_INFO); ex_psd->startScanFp(); dtracker->emptyMap(); break; case DECT_SUBCMD_SCAN_PP: _MSG("DECT_CMD_SCAN PP", MSGFLAG_INFO); ex_psd->startScanPp(); dtracker->emptyMap(); break; case DECT_SUBCMD_SCAN_CALLS: char station[32]; _MSG("DECT_CMD_SCAN CALLS", MSGFLAG_INFO); sprintf(station, "Station %.2x:%.2x:%.2x:%.2x:%.2x", rfpi[0], rfpi[1], rfpi[2], rfpi[3], rfpi[4]); _MSG(string(station), MSGFLAG_INFO); ex_psd->startScanCalls(rfpi, arg); dtracker->emptyMap(); break; default: _MSG("Bad DECT_CMD_SCAN subcommand.", MSGFLAG_ERROR); break; } break; default: _MSG("Bad DECT client command.", MSGFLAG_ERROR); return 0; } return 1; } int dect_chain_hook(CHAINCALL_PARMS) { return ((DectTracker *) auxdata)->chain_handler(in_pack); } int dect_timer(TIMEEVENT_PARMS) { // All we do is send the proto to everyone ((DectTracker *) parm)->BlitDECTProto(-1); return 1; } // All the enable function does is send everything we have already void Protocol_DECT_enable(PROTO_ENABLE_PARMS) { ((DectTracker *) data)->BlitDECTProto(in_fd); } void RFPItoString(string &out, uint8_t RFPI[5]) { char tmp[16]; snprintf(tmp, 16, "%.2x:%.2x:%.2x:%.2x:%.2x", RFPI[0], RFPI[1], RFPI[2], RFPI[3], RFPI[4]); out = tmp; } // We get a ptr to a dect tracked record, cache system, etc. Just fill it in. int Protocol_DECT(PROTO_PARMS) { dect_data_t *dect = (dect_data_t *) data; // Alloc the cache to size cache->Filled(field_vec->size()); for (unsigned int x = 0; x < field_vec->size(); x++) { unsigned int fnum = (*field_vec)[x]; if (fnum >= DECT_maxfield) { out_string = "Unknown field requested"; return -1; } if (cache->Filled(fnum)) { out_string += cache->GetCache(fnum) + " "; continue; } string rfpi; // Fill in and cache switch (fnum) { case DECT_RFPI: RFPItoString(rfpi, dect->sdata.RFPI); cache->Cache(fnum, rfpi); break; case DECT_RSSI: cache->Cache(fnum, IntToString(dect->sdata.RSSI)); break; case DECT_channel: cache->Cache(fnum, IntToString(dect->sdata.channel)); break; case DECT_first_seen: cache->Cache(fnum, IntToString(dect->first_seen)); break; case DECT_last_seen: cache->Cache(fnum, IntToString(dect->last_seen)); break; case DECT_count_seen: cache->Cache(fnum, IntToString(dect->count_seen)); break; } out_string += cache->GetCache(fnum) + " "; } return 1; } // This portion has to be an extern c for symbol matching extern "C" { int kis_plugin_info(plugin_usrdata *data) { data->pl_name = "DECT"; data->pl_version = "1.0.0"; data->pl_description = "DECT sniffer interface"; data->pl_unloadable = 0; // We can't be unloaded because we defined a source data->plugin_register = dect_register; data->plugin_unregister = dect_unregister; return 1; } } int dect_unregister(GlobalRegistry *in_globalreg) { return 0; } int dect_register(GlobalRegistry *globalreg) { // Add the dect packet component to the chain registry dect_comp_datachunk = globalreg->packetchain->RegisterPacketComponent("DECTDATA"); // Add the channels globalreg->sourcetracker->AddChannelList("DECT:0,1,2,3,4,5,6,7,8,9"); // Hopefully this won't break since we don't knw about memory management // of packetsources PacketSource_Dect *psd = new PacketSource_Dect(globalreg); if (!psd) { return -1; } // Add the packet source if (globalreg->sourcetracker->RegisterPacketSource(psd) < 0 || globalreg->fatal_condition) { _MSG("Failed to add DECT source\n", MSGFLAG_ERROR); return -1; } // Make a tracker DectTracker *dtracker; dtracker = new DectTracker(globalreg); if (!dtracker) { return -1; } DectCcc *dc = new DectCcc; if (!dc) { return -1; } dc->dtracker = dtracker; dc->psd = psd; new Dumpfile_Dectpcap(globalreg); globalreg->kisnetserver->RegisterClientCommand("DECT", dect_cc_callback, dc); return 1; }