/* * Mausezahn - A fast versatile traffic generator * Copyright (C) 2008-2010 Herbert Haas * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License version 2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program; if not, see http://www.gnu.org/licenses/gpl-2.0.html * */ // -- TOC: -- // // struct mops * mops_init () // struct mops * mops_alloc_packet (struct mops *cur) // struct mops * mops_delete_packet (struct mops *cur) // int mops_reset_packet (struct mops *cur) // // int mops_dump_all (struct mops* list) // struct mops * mops_search_name (struct mops* list, char* key) // struct mops * mops_search_id (struct mops* list, int key) // void mops_delete_all (struct mops* list) // void mops_cleanup (struct mops* list) // // int mops_set_defaults (struct mops *mp) // int mops_print_frame (struct mops *mp, char *str) // // int mops_get_new_pkt_id (struct mops *mp) // int mops_clear_layers (struct mops *mp) // int mops_get_device_index (char *devname) // int mops_use_device (struct mops * mp, int i) // int mops_get_proto_info (struct mops * mp, char *layers, char *proto) #include "mz.h" #include "mops.h" unsigned int min_frame_s; unsigned int max_frame_s; struct automops * amp_head; struct mops *mp_head; // This global will point to the head of the mops list // Creates first element, aka "head" element // This element can also be used! See mops_alloc_packet! // struct mops * mops_init(void) { // these defaults can be changed by the user: min_frame_s = MIN_MOPS_FRAME_SIZE; // important global; depends on used packet tx subsystem such as libnet max_frame_s = MAX_MOPS_FRAME_SIZE-MOPS_SIZE_MARGIN; // Create initial mops element: struct mops *new_mops = (struct mops*) malloc(sizeof(struct mops)); new_mops->next = new_mops; new_mops->prev = new_mops; new_mops->state = MOPS_STATE_NULL; new_mops->id = 0; // importante! mops_set_defaults (new_mops); strncpy(new_mops->packet_name, "-------", 8); return new_mops; } // Returns pointer to new mops element: // 1) either insert a new mops element in list // 2) or returns same pointer again if current mops element is empty // Note that new element N is always PREPENDED to cur: // ... = N-2 = N-1 = N = cur = 1 = 2 = ... // // // RETURN VALUE: + Pointer to new mops // - NULL upon failure struct mops * mops_alloc_packet(struct mops *cur) { int j; struct mops *new_mops; int new_pkt_id, pkt_id_name; char pname[MAX_MOPS_PACKET_NAME_LEN]; if (cur->state == MOPS_STATE_NULL) { // allows to use first packet in list new_mops = cur; // current mops was unused => no need to insert a new mops! } else { // create new mops element new_mops = (struct mops *) malloc(sizeof(struct mops)); if (new_mops==NULL) { fprintf(stderr, "MZ alert: cannot create new mops entry - memory full?\n"); return NULL; // memory full? } } new_mops->state = MOPS_STATE_INIT; // Assign unique packet id new_pkt_id = mops_get_new_pkt_id (cur); if (new_pkt_id==-1) return NULL; new_mops->id = new_pkt_id; // Assign unique packet name pkt_id_name = new_pkt_id; do { sprintf(pname, "PKT%04d", pkt_id_name); pkt_id_name++; } while (mops_search_name (mp_head, pname)); // check if this name is really unique strncpy(new_mops->packet_name, pname, MAX_MOPS_PACKET_NAME_LEN); // append to doubly linked list new_mops->prev = cur->prev; new_mops->next = cur; cur->prev = new_mops; new_mops->prev->next = new_mops; mops_set_defaults (new_mops); // set header parametes (addresses etc) // Reset protocol descriptor new_mops->p_desc = NULL; new_mops->p_desc_type = MOPS_NO_PDESC; // clear counter values new_mops->used_counters=0; for (j=0; jcounter[j].use = 0; new_mops->counter[j].offset = 0; new_mops->counter[j].random = 0; } return new_mops; } // Delete particular packet (remove it from list). // // If mp_head is deleted, makes previous element mp_head. // Note that the global mp_head must exist but within MOPS this // is always the case. // // Returns pointer to previous element in the list // or NULL if packet is active struct mops * mops_delete_packet(struct mops *cur) { struct mops *last; if (mops_is_active(cur)) { mops_destroy_thread(cur); } mops_ext_del_pdesc (cur); // delete p_desc (if available) // remove automops data if available if (cur->amp != NULL) { free(cur->amp); cur->amp=NULL; } if (cur->amp_pdu != NULL) { free (cur->amp_pdu); cur->amp_pdu=NULL; } last = cur->prev; cur->next->prev = cur->prev; cur->prev->next = cur->next; if (cur==mp_head) { mp_head = last; } if (cur!=NULL) { free (cur); cur=NULL; } return last; } // Erase all data of a mops entry and even chooses a new standard name // DOES NOT delete the entry from the list // int mops_reset_packet(struct mops *cur) { int i=0; char pname[16]; // stop thread if necessary if (mops_is_active(cur)) { mops_destroy_thread(cur); } // remove pdesc if available mops_ext_del_pdesc (cur); cur->state = MOPS_STATE_NULL; // remove automops data if available if (cur->amp != NULL) { free(cur->amp); cur->amp=NULL; } if (cur->amp_pdu != NULL) { free (cur->amp_pdu); cur->amp_pdu=NULL; } // find another name do { sprintf(pname, "PKT%04d", i); i++; } while (mops_search_name (mp_head, pname)); // check if this name is really unique strncpy(cur->packet_name, pname, MAX_MOPS_PACKET_NAME_LEN); // Place everything else in this function: mops_set_defaults (cur); return 0; } // Runs through all packets and dumps some statistics into 'str' // Returns 1 if only the uninitialized head is available // int mops_dump_all(struct mops* list, char *str) { struct mops *head = list; struct mops *cur = list; char output[100]; int anzmops=0, active=0, config=0, raw=0, ival=0; do { if (cur->state == MOPS_STATE_ACTIVE) { active++; } else if (cur->state == MOPS_STATE_CONFIG) { config++; } else if (cur->interval_used==2) { ival++; } if (cur->use_ETHER == 0) raw++; anzmops++; cur = cur->next; } while (head != cur); snprintf(output, 99, "%i Mopse(s) (interval: %i, active: %i, config: %i, raw: %i)", anzmops, ival, active, config, raw); strncpy(str, output, 99); if ((!active) && (!config)) return 1; return 0; } // Search for key = name and return pointer to that mops // Return NULL if not found struct mops * mops_search_name (struct mops* list, char *key) { struct mops *head = list; struct mops *cur = list; do { if ( (strncasecmp(key, cur->packet_name, MAX_MOPS_PACKET_NAME_LEN) == 0)) { return cur; // FOUND! } cur = cur->next; } while (head != cur); return NULL; // NOT FOUND! } // Search for key = id and return pointer to that mops // Return NULL if not found struct mops * mops_search_id (struct mops* list, u_int32_t key) { struct mops *head = list; struct mops *cur = list; do { if ( cur->id == key ) { return cur; // FOUND! } cur = cur->next; } while (head != cur); return NULL; // NOT FOUND! } // Deletes all elements except the specified element which us usually // the head element. Also ACTIVE elements will be removed and the // corresponding threads will be stopped. // // Thus the list can grow again later via mops_alloc_packet // void mops_delete_all(struct mops* list) { struct mops *head = list; struct mops *cur = list->next; struct mops *tmp; // Delete all but head element: while (head != cur) { tmp = cur->next; mops_ext_del_pdesc (cur); // delete p_desc (if available) mops_destroy_thread(cur); // remove automops data if available if (cur->amp != NULL) { free(cur->amp); cur->amp=NULL; } if (cur->amp_pdu != NULL) { free (cur->amp_pdu); cur->amp_pdu=NULL; } cur->amp_pdu_s=0; if (cur!=NULL) { free(cur); cur=NULL; } cur = tmp; } head->next = head; head->prev = head; head->state = MOPS_STATE_NULL; } // Same as mops_delete_all but also destroys the head element: void mops_cleanup(struct mops* list) { mops_delete_all(list); mops_ext_del_pdesc (list); // delete p_desc (if available) mops_destroy_thread(list); if (list!=NULL) { free(list); list=NULL; } } // Set default MOPS and protocol header parameters // Currently most parameters are taken from the legacy tx-structure // // NOTE: Does NOT and should NOT change the packet_name !!! // Because user might be confused if it is changed to something // unexpected such as 'PKT0341'. // // TODO: find out MAC of default GW int mops_set_defaults (struct mops *mp) { // Initialize frame arrays with zero bytes memset(mp->frame, 0x00, MAX_MOPS_FRAME_SIZE); memset(mp->msg, 0x00, MAX_MOPS_MSG_SIZE); // Basics -- MOPS Management Parameters pthread_mutex_init (& mp->mops_mutex, NULL); // mp->mops_thread = 0; // TODO // mp->interval_thread = 0; // TODO mp->verbose = 1; // normal verbosity mp->use_ETHER = 0; mp->use_SNAP = 0; mp->use_dot1Q = 0; mp->use_MPLS = 0; mp->use_IP = 0; mp->use_UDP = 0; mp->use_TCP = 0; mp->frame_s = 0; mp->msg_s = 0; mp->description[0]='\0'; mp->auto_delivery_off = 0; mp->mz_system = 0; strncpy (mp->device, tx.device, 16); mp->count = 0; mp->cntx = 0; mp->ndelay.tv_sec = 0; mp->ndelay.tv_nsec = 100000000L; // 100 ms default delay mp->interval_used = 0; mp->interval.tv_sec = 0; mp->interval.tv_nsec = 0; mp->delay_sigma.tv_sec = 0; mp->delay_sigma.tv_nsec = 0; mp->MSG_use_RAW_FILE=0; mp->MSG_use_HEX_FILE=0; mp->MSG_use_ASC_FILE=0; mp->fp=NULL; mp->chunk_s = MAX_MOPS_MSG_CHUNK_SIZE; // TODO: check if amp and amp_header is free()'d in any case!!! mp->amp = NULL; mp->amp_pdu = NULL; mp->amp_pdu_s = 0; // Ethernet defaults: memcpy((void *) &mp->eth_dst, (void *) &tx.eth_dst, 6); memcpy((void *) &mp->eth_src, (void *) &tx.eth_src, 6); mp->eth_type = 0x800; mp->eth_src_israndom = 0; mp->dot1Q_isrange = 0; mp->mpls_isrange = 0; // IP defaults: // abuse our hton: here we actually convert from net to host order: mops_hton4 ((u_int32_t*) &tx.ip_dst, (u_int8_t*) &mp->ip_dst); mops_hton4 ((u_int32_t*) &tx.ip_src, (u_int8_t*) &mp->ip_src); // Note that the IP address of the "default interface" is assigned to that mops. // If the mops is bind to another interface then use the associated interface. // Implement this in cli_packet and function cmd_packet_bind // mp->ip_version = 4; mp->ip_IHL = 0; mp->ip_len = 20; mp->ip_tos = 0; mp->ip_flags_RS=0; // 0|1 ... Reserved flag "must be zero" mp->ip_flags_DF=0; // 0|1 ... Don't Fragment mp->ip_flags_MF=0; // 0|1 ... More Fragments mp->ip_frag_offset=0; mp->ip_fragsize=0; // fragmentation OFF mp->ip_frag_overlap=0; // no overlapping fragments mp->ip_ttl = 255; mp->ip_proto = 17; // UDP mp->ip_src_israndom = 0; mp->ip_src_isrange = 0; mp->ip_dst_isrange = 0; mp->ip_option_used = 0; mp->ip_IHL_false = 0; mp->ip_len_false = 0; mp->ip_sum_false = 0; mp->ip_option_used = 0; mp->ip_option_s = 0; // L4 defaults (port numbers) mp->sp=0; mp->sp_start=0; mp->sp_stop=0; mp->sp_isrand=0; mp->sp_isrange=0; mp->dp=0; mp->dp_start=0; mp->dp_stop=0; mp->dp_isrand=0; mp->dp_isrange=0; // UDP defaults // mp->udp_len_false = 0; mp->udp_sum_false = 0; mp->udp_sum = 0xffff; // this default means "transmitter didn't compute checksum" // TCP defaults // mp->tcp_seq = 0xcafebabe; mp->tcp_seq_delta = 0; // no range mp->tcp_seq_start = 0; mp->tcp_seq_stop = 0xffffffff; mp->tcp_ack = 0; mp->tcp_ack_delta = 0; // no range mp->tcp_ack_start = 0; mp->tcp_ack_stop = 0xffffffff; mp->tcp_win = 100; mp->tcp_sum_false = 0; mp->tcp_offset_false = 0; mp->tcp_offset = 0; mp->tcp_sum = 0xffff; // this default means "transmitter didn't compute checksum" mp->tcp_option_used = 0; mp->tcp_option_s =0; mp->tcp_ctrl_CWR =0; mp->tcp_ctrl_ECE =0; mp->tcp_ctrl_URG =0; mp->tcp_ctrl_ACK =0; mp->tcp_ctrl_PSH =0; mp->tcp_ctrl_RST =0; mp->tcp_ctrl_SYN =1; // assume that we begin with a TCP SYN mp->tcp_ctrl_FIN =0; mp->tcp_urg =0; mp->tcp_ack =0; mp->tcp_res =0; return 0; } int mops_print_frame (struct mops *mp, char *str) { int i=0, fs; char octet[8], lnr[8], hex[MAX_MOPS_FRAME_SIZE*3]; hex[0]=0x00; if (! (fs = mp->frame_s) ) return -1; // frame length zero (no frame?) if (fs>1) { sprintf(lnr,"%4i ",i+1); strcat(hex, lnr); for (i=0; i0) && (!(i%8))) { strcat(hex, " "); // insert space after each 8 bytes hex[strlen(hex)-2]=' '; } if ((i>0) && (!(i%MAX_CLI_LINE_BYTES))) { sprintf(lnr,"\n%4i ",i+1); strcat(hex, lnr); } sprintf(octet, "%02x:", mp->frame[i]); strcat(hex, octet); } } hex[strlen(hex)-1]=' '; strcpy(str, hex); return 0; } // Find and returns a new unique packet id // If none can be found, returns -1. // int mops_get_new_pkt_id (struct mops *list) { struct mops *head = list; struct mops *cur = list; int i, min=0xffffffff, max=0; do { if (cur->id < min) min = cur->id; // determine current min id if (cur->id > max) max = cur->id; // determine current max id cur = cur->next; } while (head != cur); if (min>0) i= min-1; else i = max+1; // just for paranoia: check again if unique! do { if (cur->id == i) { return -1; // } cur = cur->next; } while (head != cur); return i; } // Simply sets specified 'layer switches' in mops struct // (use_ETHER, use_IP, ...) to zero. // // RETURN VALUE: tells which layers had been configured before clearing. // // The presence of the layers is indicated via binary coding: // // MOPS_ALL 127 // clear all // MOPS_ETH 1 // MOPS_SNAP 2 // either LLC, LLC+SNAP // MOPS_dot1Q 4 // MOPS_MPLS 8 // MOPS_IP 16 // MOPS_UDP 32 // MOPS_TCP 64 // int mops_clear_layers (struct mops *mp, int l) { int ret=0; if (l & MOPS_ETH) { if (mp->use_ETHER) ret+=1; mp->use_ETHER = 0; } if (l & MOPS_SNAP) { if (mp->use_SNAP) ret+=2; mp->use_SNAP = 0; } if (l & MOPS_dot1Q) { if (mp->use_dot1Q) ret+=4; mp->use_dot1Q = 0; } if (l & MOPS_MPLS) { if (mp->use_MPLS) ret+=8; mp->use_MPLS = 0; } if (l & MOPS_IP) { if (mp->use_IP) ret+=16; mp->use_IP = 0; } if (l & MOPS_UDP) { if (mp->use_UDP) ret+=32; mp->use_UDP = 0; } if (l & MOPS_TCP) { if (mp->use_TCP) ret+=64; mp->use_TCP = 0; } return ret; } // Get global device index for a given device name. // // RETURN VALUE: // Either the desired device index or -1 if not found. // // EXAMPLE: // i = mops_get_device_index("eth0") // int mops_get_device_index(char *devname) { int i; for (i=0; ieth_src, (void *) &device_list[i].mac_mops[0], 6); memcpy((void *) &mp->ip_src, (void *) &device_list[i].ip_mops[0], 4); return 0; } // Creates two strings as used by the 'show packet' command, // 1) one identifying all used layers of a packet, // 2) the other which higher layer protocol is used // // caller must define: // char layers[16], proto[16]; // // RETURNS 0 upon success, 1 upon failure. // int mops_get_proto_info(struct mops *mp, char *layers, char *proto) { char ds[16], pr[16]; if (mp==NULL) return 1; ds[0]='\0'; pr[0]='\0'; if (mp->use_ETHER) strcat(ds,"E"); else strcat(ds,"-"); if (mp->use_SNAP) strcat(ds,"S"); else strcat(ds,"-"); if (mp->use_dot1Q) strcat(ds,"Q"); else strcat(ds,"-"); if (mp->use_MPLS) strcat(ds,"M"); else strcat(ds,"-"); if (mp->use_IP) { if (mp->auto_delivery_off) strcat(ds,"i"); else strcat(ds,"I"); } else strcat(ds,"-"); if (mp->use_UDP) strcat(ds,"U"); else if (mp->use_TCP) strcat(ds,"T"); else strcat(ds,"-"); switch (mp->p_desc_type) { case MOPS_ARP: strncpy(pr, "ARP", 8); break; case MOPS_BPDU: strncpy(pr, "BPDU", 8); break; case MOPS_CDP: strncpy(pr, "CDP", 8); break; case MOPS_DNS: strncpy(pr, "DNS", 8); break; case MOPS_ICMP: strncpy(pr, "ICMP", 8); break; case MOPS_IGMP: strncpy(pr, "IGMP", 8); break; case MOPS_LLDP: strncpy(pr, "LLDP", 8); break; case MOPS_RTP: strncpy(pr, "RTP", 8); break; case MOPS_SYSLOG: strncpy(pr, "SYSLOG", 8); break; default: break; } strncpy(layers, ds, 16); strncpy(proto, pr, 16); return 0; }