diff options
Diffstat (limited to 'staging')
62 files changed, 28650 insertions, 0 deletions
diff --git a/staging/automops.c b/staging/automops.c new file mode 100644 index 0000000..b465346 --- /dev/null +++ b/staging/automops.c @@ -0,0 +1,1013 @@ +/* + * 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 + * +*/ + +#include "mz.h" +#include "mops.h" + + +// Creates first element, aka "head" element +// This element can also be used! See automops_alloc_protocol! +// +struct automops * automops_init() +{ + // Create initial automops element: + struct automops *new_automops = (struct automops*) malloc(sizeof(struct automops)); + new_automops->next = new_automops; + new_automops->prev = new_automops; + automops_set_defaults (new_automops); + new_automops->used = -1; // freshly created, no valid data in it + return new_automops; +} + + + +// (Re-)sets anything within the specified automops element +void automops_set_defaults(struct automops * cur) +{ + int i; + + mz_strncpy(cur->name, "user_proto", 16); + mz_strncpy(cur->desc, "undefined", 16); + cur->layers_on = 0; + cur->layers_off = 0; + + cur->etype = 0; + cur->proto = 0; + for (i=0; i<6; i++) { + cur->sa[i] = 0x00; + cur->da[i] = 0xff; // bcast (silly?) + } + cur->SA = cur->DA = 0; + cur->sp = cur->dp = 0; + + cur->payload_type = 0; // both ascii or hex + cur->payload = NULL; + cur->payload_s = 0; + cur->defined_externally = -1; // undefined + if (cur->field != NULL) automops_delete_fields (cur); + cur->field = NULL; +} + + +// Returns pointer to new automops element: +// 1) either insert a new automops element in list +// 2) or returns same pointer again if current automops element is empty +// Note that new element N is always PREPENDED to cur: +// ... = N-2 = N-1 = N = cur = 1 = 2 = ... +// Therefore, cur should be typically a pointer to the head element +// +struct automops * automops_alloc_protocol(struct automops *cur) +{ + struct automops *new_automops; + + if (cur->used == -1) // allows to use head element in list + { + new_automops = cur; // current automops was unused => no need to insert a new one! + } + else // create new automops element + { + new_automops = (struct automops *) malloc(sizeof(struct automops)); + if (new_automops==NULL) + { + fprintf(stderr, "MZ alert: cannot create new automops entry - memory full?\n"); + return NULL; // memory full? + } + new_automops->field=NULL; // don't remove this! See automops_set_defaults() to understand. + automops_set_defaults(new_automops); + } + + new_automops->used=0; + + // append to doubly linked list + new_automops->prev = cur->prev; + new_automops->next = cur; + cur->prev = new_automops; + new_automops->prev->next = new_automops; + + return new_automops; +} + + +// Delete particular protocol (remove it from list or mops). +// +// If amp_head is deleted, makes previous element amp_head. +// Note that the global amp_head must exist but within MOPS this +// is always the case. +// +// RETURN VALUE: +// +// - pointer to previous element in the list +// - NULL if current automops is used by some mops(es) +// (in this case, we may still need it, maybe the user wants +// to modify data or wants other information...?) +// +// - NULL if current element is a single element attached to a mops +// +struct automops * automops_delete_protocol(struct automops *cur) +{ + struct automops *last; + + // Maybe the following is not really practical? ///// + if (cur->used>0) { + return NULL; + } + ///////////////////////////////////////////////////// + + // delete fields list: + automops_delete_fields (cur); + + if (cur->payload_s) free (cur->payload); + + if ((cur!=amp_head) && (cur->prev==NULL) && (cur->next==NULL)) { + // this one is attached to a mops + if (cur!=NULL) free (cur); + return NULL; + } + + // part of linked list + last = cur->prev; + cur->next->prev = cur->prev; + cur->prev->next = cur->next; + if (cur==amp_head) { + amp_head = last; + } + if (cur!=NULL) free (cur); + + return last; +} + + + +// Search automops element for a given protocol name +// +// Returns pointer to that automops element +// or NULL if not found +// +struct automops * automops_search_protocol(struct automops *list, char *name) +{ + struct automops *head = list; + struct automops *cur = list; + + do { + if ( (strncasecmp(name, cur->name, + AUTOMOPS_MAX_NAME_LEN) == 0)) { + return cur; // FOUND! + } + cur = cur->next; + } while (head != cur); + + return NULL; // NOT FOUND! +} + + + +// Runs through all automops entries and dumps some basic info +// Returns the number of used protocols +// +int automops_dump_all(struct automops* list) +{ + struct automops *head = list; + struct automops *cur = list; + struct fields *f=NULL; + int anzmops=0, used=0; + char str[64], ft[32]; + uint32_t SA=0, DA=0; + uint8_t *x, *y; + char bits_on[18], bits_off[18]; + int i=0, j=0; + + do { + if (cur->used==-1) { + fprintf(stderr, "AUTOMOPS: Initial element\n"); + if ((cur->next==cur)&&(cur->prev==cur)) + fprintf(stderr, " No other elements found.\n"); + break; + } + if (cur->used>0) used++; + anzmops++; + SA=ntohl(cur->SA); x = (uint8_t*) &SA; + DA=ntohl(cur->DA); y = (uint8_t*) &DA; + char2bits(cur->layers_on, bits_on); + char2bits(cur->layers_off, bits_off); + fprintf(stderr, "Protocol %i: %s -- %s\n" + " Layercodes: X T U I M Q S E\n" + " requires %s (0x%02x)\n" + " conflicts %s (0x%02x)\n" + " L2: EtherType=%04x, sa=%02x:%02x:%02x:%02x:%02x:%02x, da=%02x:%02x:%02x:%02x:%02x:%02x\n" + , + anzmops, cur->name, cur->desc, + bits_on, cur->layers_on, bits_off, cur->layers_off, + cur->etype, cur->sa[0], cur->sa[1], cur->sa[2], cur->sa[3], cur->sa[4], cur->sa[5], + cur->da[0], cur->da[1], cur->da[2], cur->da[3], cur->da[4], cur->da[5]); + if (cur->layers_on&MOPS_IP) { + fprintf(stderr, " IP: proto=%i, SA=%u.%u.%u.%u, DA=%u.%u.%u.%u\n", + cur->proto, *x, *(x+1), *(x+2), *(x+3), *y, *(y+1), *(y+2), *(y+3)); + } else { + fprintf(stderr, " IP: ---\n"); + } + // Walk through field data: + f=cur->field; j=0; + while (f!=NULL) { + j++; // count number of fields + if (verbose) { + i=0; + if (f->longdesc!=NULL) { + mz_strncpy(str, f->longdesc, 60); + if (strnlen(str,60)>=59) i=1; + } + else { + mz_strncpy(str, "-- no long description specified --", 60); + } + amp_type2str(f->type, ft); + fprintf(stderr, " %02i Field [%i] '%s' -- %s\n" + " Description: %s%s\n" + " Type: %s %s %s (%lu/%lu) {%lu..%lu..%lu} shift: %i; %i chars\n" + ,f->i, f->index, f->name, f->shortdesc, + str, (i) ? "..." : "", + ft, (f->constant) ? "FIXED" : "", + (f->valname!=NULL) ? f->valname : "(no value name)" , + (long unsigned int) f->tlv_type, + (long unsigned int) f->tlv_len, + (long unsigned int) f->min, + (long unsigned int) f->val, + (long unsigned int) f->max, + f->leftshift, f->str_s); + } + f=f->next; + } + if (verbose==0) fprintf(stderr, " %i fields defined.\n", j); + //--------------------------------- + cur = cur->next; + } while (head != cur); + + return used; +} + + + +// Creates an independent automops element for mops +// (it will be not part of any linked list so, next=prev=NULL) +// +// RETURN VALUE: - Pointer to the cloned automops +// - NULL upon failure +// +struct automops * automops_clone_automops(struct automops * amp) +{ + struct automops *new_automops; + struct fields *f, *g, *h=NULL; + int i; + + // Allocate memory + new_automops = (struct automops *) malloc(sizeof(struct automops)); + if (new_automops==NULL) { + fprintf(stderr, "MZ alert: cannot create new automops element - memory full?\n"); + return NULL; // memory full? + } + + // Copy the automops items + new_automops->next = NULL; + new_automops->prev = NULL; + + strncpy(new_automops->name, amp->name, AUTOMOPS_MAX_NAME_LEN); + strncpy(new_automops->desc, amp->desc, AUTOMOPS_MAX_SHORTDESC_LEN); + new_automops->layers_on = amp->layers_on; + new_automops->layers_off = amp->layers_off; + new_automops->etype = amp->etype; + new_automops->proto = amp->proto; + for (i=0; i<6; i++) { + new_automops->da[i] = amp->da[i]; // dst mac + new_automops->sa[i] = amp->sa[i]; // src mac + } + new_automops->DA = amp->DA; // dst IP + new_automops->SA = amp->SA; // src IP + new_automops->dp = amp->dp; // dst port + new_automops->sp = amp->sp; // src port + new_automops->defined_externally = amp->defined_externally; + new_automops->payload_type = amp->payload_type; + if (amp->payload_s) { + new_automops->payload = (char*) malloc(amp->payload_s); + if (new_automops->payload==NULL) { + fprintf(stderr, "MZ alert: cannot create new automops payload element - memory full?\n"); + return NULL; // memory full? + } + memcpy((void*) new_automops->payload, amp->payload, amp->payload_s); + } + + new_automops->used = amp->used; + + //////////////////////////////////////////////////////////////////////////////////////////////// + // + // Copy the fields list + // + new_automops->field = NULL; + for (f=amp->field; f!=NULL; f=f->next) { + g = (struct fields *) malloc(sizeof(struct fields)); + if (g==NULL) { + fprintf(stderr, "MZ alert: cannot create new field element - memory full?\n"); + return NULL; // memory full? + } + if (new_automops->field==NULL) { // first element + new_automops->field = g; + h = g; + } else { // next elements + h->next = g; + h = g; + } + // copy all data. From here on 'h' is the new one, 'f' is the existing one + mz_strncpy(h->name, f->name, AUTOMOPS_MAX_NAME_LEN); + mz_strncpy(h->shortdesc, f->shortdesc, AUTOMOPS_MAX_SHORTDESC_LEN); + mz_strncpy(h->valname, f->valname, AUTOMOPS_MAX_NAME_LEN); + if (f->longdesc!=NULL) { + h->longdesc = (char*) + malloc(strnlen(f->longdesc, 1600)); // 80 chars x 20 lines should be enough + if (h->longdesc == NULL) { + fprintf(stderr, "MZ alert: cannot allocate memory!\n"); + return NULL; // memory full? + } + strncpy(h->longdesc, f->longdesc, 1600); + } + if (f->str_s) { + h->str_s = f->str_s; + h->str = (u_int8_t *) malloc(f->str_s); + if (h->str == NULL) { + fprintf(stderr, "MZ alert: cannot allocate memory!\n"); + return NULL; // memory full? + } + memcpy((void*) h->str, (void*) f->str, f->str_s); + } + h->constant = f->constant; + h->type = f->type; + h->tlv_type = f->tlv_type; + h->tlv_len = f->tlv_len; + h->val = f->val; + h->min = f->min; + h->max = f->max; + h->leftshift = f->leftshift; + h->index = f->index; + } + return new_automops; +} + + +// Add a new field object +struct fields * automops_add_field (struct automops *amp) +{ + struct fields *f, *f_prev=NULL, *g; + int i=0; + + // jump to the end of the fields list + f=amp->field; + while (f!=NULL) { + f_prev=f; + ++i; + f=f->next; + } + + g = (struct fields *) malloc(sizeof(struct fields)); + if (g==NULL) { + if (verbose) fprintf(stderr, "MZ alert: cannot create new field element - memory full?\n"); + return NULL; // memory full? + } + + if (amp->field==NULL) { // is is first element in amp + amp->field = g; + } else { // it is just another element in the fields list + f_prev->next = g; + } + g->next=NULL; // 'pointing to NULL' identifies the last element + g->i=i; // each field element has a unique internal number + g->index=0; // indicates 'empty' field + automops_field_set_defaults(g); + return g; +} + + +// Typically only used by automops_add_field() +// Only call this function after creating a new field element +void automops_field_set_defaults(struct fields *f) +{ + f->name[0]=0x00; + f->shortdesc[0]=0x00; + f->longdesc=NULL; + f->constant=0; + + //NOTE: f->i MUST NOT be reset! + f->index=0; + f->valname[0]=0x00; + f->tlv_type=0; + f->tlv_len=0; + f->val=0; + f->min=0; + f->max=0; + f->leftshift=0; + f->str=NULL; + f->str_s=0; + f->next=NULL; +} + + +// Returns integer equivalent for a string of basic protocols. +// For example returns MOPS_ETH | MOPS_IP for d="eth ip". +// See the definitions in mops.h. +// +// NOTE: Does (and must) NOT verify whether items are conflicting +// such as "udp tcp". This task MUST be done by callee, otherwise +// this function's purpose would be not generic enough. +// +// RETURN VALUE: +// The sum of basic protocols +// or -1 upon failure. +int mops_str2layers(char *d) +{ + int ret=0; + char *tok; + + // dissalow too long strings. + if (strlen(d)>50) return -1; // TODO: replace 100 to a more reasonable value + + tok=strtok(d, " "); + while (tok!=NULL) { + if (strncasecmp("eth", d, 10)==0) ret |= MOPS_ETH; + else + if (strncasecmp("snap", d, 10)==0) ret |= MOPS_SNAP; + else + if (strncasecmp("dot1q", d, 10)==0) ret |= MOPS_dot1Q; + else + if (strncasecmp("mpls", d, 10)==0) ret |= MOPS_MPLS; + else + if (strncasecmp("ip", d, 10)==0) ret |= MOPS_IP; + else + if (strncasecmp("udp", d, 10)==0) ret |= MOPS_UDP; + else + if (strncasecmp("tcp", d, 10)==0) ret |= MOPS_TCP; + else + return -1; // unknown + tok=strtok(NULL, " "); + } + return ret; +} + +// Returns one of 'enum fieldtypes' for a given ascii string +// or -1 if unknown field type given. +int amp_str2type(char *d) +{ + if (strncasecmp("byte8", d, 10)==0) return Byte8; + if (strncasecmp("byte16", d, 10)==0) return Byte16; + if (strncasecmp("byte32", d, 10)==0) return Byte32; + if (strncasecmp("flaginbyte", d, 16)==0) return Flag_in_Byte; + if (strncasecmp("multibytes", d, 16)==0) return MultiBytes; + if (strncasecmp("multibyteshex", d, 16)==0) return MultiBytesHex; + if (strncasecmp("tlv", d, 10)==0) return TLV; + return -1; +} + +// Converts integer field types into ascii string s[32]. +// Returns 0 upon success, 1 if unknown type +int amp_type2str(int t, char *s) +{ + switch (t) { + case Byte8: + mz_strncpy(s, "Byte8", 32); + break; + case Byte16: + mz_strncpy(s, "Byte16", 32); + break; + case Byte32: + mz_strncpy(s, "Byte32", 32); + break; + case Flag_in_Byte: + mz_strncpy(s, "FlagInByte", 32); + break; + case MultiBytes: + mz_strncpy(s, "MultiBytes", 32); + break; + case MultiBytesHex: + mz_strncpy(s, "MultiBytesHex", 32); + break; + case TLV: + mz_strncpy(s, "TLV", 32); + break; + default: + mz_strncpy(s, "[unknown/same]", 32); + return 1; + } + return 0; +} + + +// Searches the automops object with specified name 'd'. +// NOTE: The names are case insensitive! +// +// RETURN VALUE: pointer to that object +// or NULL if not found +// +struct automops * amp_getamp_byname(struct automops *head, char *d) +{ + struct automops *a; + a = head; + do { + if (strncasecmp(a->name, d, AUTOMOPS_MAX_NAME_LEN)==0) return a; + a=a->next; + } while (a!=head); + return NULL; // not found +} + + +// Add data 'd' identified by tag 'xntag' to the automops entry 'amp'. +// +// RETURN VALUE: 0 upon success, 1 upon failure +// +int amp_add_pentry (struct automops *amp, int xntag, char *d) +{ + int i=0; + char *tok; + u_int8_t x[MAX_MOPS_MSG_SIZE]; + struct automops *g; + + switch (xntag) { + case xml_name: + if (strpbrk(d," \t")!=NULL) return ampInvalidName; // name must not consist of multiple words! + g = amp_getamp_byname(amp_head, d); + if (g!=NULL) return ampDuplicateName; // name already exists! + mz_strncpy(amp->name, d, AUTOMOPS_MAX_NAME_LEN); + if (verbose==2) { + fprintf(stderr, "Adding protocol '%s'\n", amp->name); + } + break; + + case xml_desc: + mz_strncpy(amp->desc, d, AUTOMOPS_MAX_SHORTDESC_LEN); + break; + + case xml_requires: + i = mops_str2layers(d); + if (i==-1) return ampInvalidLayer; + if ((i&MOPS_UDP) && (i&MOPS_TCP)) return ampTCPandUDP; // cannot require both! + amp->layers_on |= i; // must be ORed because several same-tags allowed + break; + + case xml_conflicts: + i = mops_str2layers(d); + if (i==-1) return ampInvalidLayer; + amp->layers_off |= i; // must be ORed because several same-tags allowed + break; + + case xml_payloadtype: // 0=none, 1=ascii, 2=hex, 3=any + tok = strtok (d," "); + while (tok!=NULL) { + if (strncasecmp("allowed", d, 10)==0) { + // only change if payload_type is still zero + if (amp->payload_type==0) amp->payload_type=3; + } else + if (strncasecmp("ascii", d, 10)==0) amp->payload_type|=1; + else + if (strncasecmp("hex", d, 10)==0) amp->payload_type|=2; + else + if (strncasecmp("any", d, 10)==0) amp->payload_type=3; + else + if (strncasecmp("none", d, 10)==0) amp->payload_type=0; + else return ampPayloadType; // unknown + tok=strtok(NULL, " "); + } + break; + + case xml_payload: + i=strnlen(d,MAX_MOPS_MSG_SIZE); + if (i==MAX_MOPS_MSG_SIZE) return ampPayloadLen; + amp->payload = (char*) malloc (i+1); + mz_strncpy(amp->payload, d, i+1); + amp->payload_s = i; + break; + + case xml_payloadhex: + i=str2hex(d,x,MAX_MOPS_MSG_SIZE); + if (i==MAX_MOPS_MSG_SIZE) return ampPayloadLen; + if (i==-1) return 1; + amp->payload = (char*) malloc (i+1); + memcpy((void*)amp->payload, (void*) x, i); + amp->payload_s = i; + break; + + default: + return ampUnknownTag; + + } + return 0; +} + +// Checks if given index value would be valid for the specified amp. +// (Index values must increase monotonic, successive same-values are +// allowed, step size is 0 or 1 but not greater. First index value +// must be 1. Example: 1,2,2,2,3,4,5,5,5,5,5,6,7,7,7.) +// +// RETURN VALUE: 0 if ok, 1 if wrong +// +int amp_checkindex(struct automops *amp, int i) +{ + int last_i=0; + struct fields *g, *h=NULL; + + g=amp->field; + while (g!=NULL) { // jump to last field object P->F1->F2->NULL + if (g->index==0) break; // stop if empty field object found + h=g; + g=g->next; + } // now h is the penultimate object +// printf("CHECKINDEX: try for %i, amp='%s' -- field '%s', index %i, [%i]\n", +// i, amp->name, h->name, h->index, h->i); + if (h==NULL) return 0; // first element, so any i is ok + last_i=h->index; + if (i<last_i) return 1; // index is decreasing! + if ((i-last_i)>1) return 1; // index increase step larger 1! + return 0; +} + + + +// Searches the field object with specified name 'd'. +// NOTE: The names ar case insensitive! +// +// RETURN VALUE: pointer to that object +// or NULL if not found +// +struct fields * amp_getfield_byname(struct automops *amp, char *d) +{ + struct fields *f; + + f = amp->field; + + while (f!=NULL) { + if (strncasecmp(f->name, d, AUTOMOPS_MAX_NAME_LEN)==0) return f; + f=f->next; + } + return NULL; // not found +} + + + +// This strange function ensures that 'w' consists of a single word. +// If 'w' consists of multiple words, it removes all but the first +// word. Additionally surrounding spaces are removed. +// +// RETURN VALUE: number of words found +// +// EXAMPLE: "Hello world" => "Hello" +// (return value = 2) +// +int ensure_single_word(char *w) +{ + char *t, *t0; + int i=0; + + t=strtok(w," "); + t0=t; + while (t!=NULL) { + i++; + t=strtok(NULL, " "); + } + mz_strncpy(w, t0, AUTOMOPS_MAX_NAME_LEN); + return i; +} + + + + +// Add data 'd' identified by tag 'xntag' to the field entry 'f' +int amp_add_fentry (struct automops *amp, struct fields *f, int xntag, char *d) +{ + int i=0; + unsigned long long int ulli=0; + struct fields *g=NULL; + + switch(xntag) { + case xml_index: + i = (int) str2int(d); + if (amp_checkindex(amp, i)) return ampInvalidIndex; // invalid index + f->index = (int) i; + break; + + case xml_name: + if (ensure_single_word(d)>1) return ampInvalidName; // name must be single word + g = amp_getfield_byname(amp, d); + if (g!=NULL) return 1; // name already exists + mz_strncpy(f->name, d, AUTOMOPS_MAX_NAME_LEN); + break; + + case xml_desc: + mz_strncpy(f->shortdesc, d, AUTOMOPS_MAX_SHORTDESC_LEN); + break; + + case xml_longdesc: + i = strnlen(d, 400); + if (i==400) return ampDescTooLong; + f->longdesc = (char*) malloc(i+1); + mz_strncpy(f->longdesc, d, i+1); + break; + + case xml_type: + i = amp_str2type(d); + if (i==-1) return ampInvalidType; + f->type = i; + break; + + case xml_constant: + if (strncasecmp(d, "yes", 6)==0) f->constant=1; + else + if (strncasecmp(d, "no", 6)==0) f->constant=0; + else return ampUnknownKeyword; // unknown keyword + break; + + case xml_valname: + if (ensure_single_word(d)>1) return ampSingleWordRequired; // name must be single word + i = strnlen(d, AUTOMOPS_MAX_NAME_LEN); + if (i==AUTOMOPS_MAX_NAME_LEN) return 1; // too long + mz_strncpy(f->valname, d, AUTOMOPS_MAX_NAME_LEN); + break; + + case xml_value: + ulli = str2lint(d); + if (ulli>0xffffffff) return ampRangeError; + f->val = (u_int32_t) ulli; + break; + + case xml_min: + ulli = str2lint(d); + if (ulli>0xffffffff) return ampRangeError; + f->min = (u_int32_t) ulli; + break; + + case xml_max: + ulli = str2lint(d); + if (ulli>0xffffffff) return ampRangeError; + if (ulli<f->min) return 1; // max must be greater or equal min + f->max = (u_int32_t) ulli; + break; + + case xml_tlvt: + ulli = str2lint(d); + if (ulli>0xffffffff) return ampRangeError; + f->tlv_type = (u_int32_t) ulli; + break; + + case xml_tlvl: + ulli = str2lint(d); + if (ulli>0xffffffff) return ampRangeError; + f->tlv_len = (u_int32_t) ulli; + break; + + case xml_lshift: + i = (int) str2int(d); + if (i>7) return ampRangeError; + f->leftshift=i; + break; + + default: + return ampUnknownTag; // unknown tag + } + return 0; +} + + +// Delete linked list of field elements for a given automops +// Returns the number of deleted elements +int automops_delete_fields (struct automops *amp) +{ + struct fields * cur = amp->field; + struct fields * tmp; + int i=0; + + if (cur==NULL) return 0; + + do { + tmp = cur; + cur = cur->next; + if (tmp->str_s) { + if (tmp->str!=NULL) { + free (tmp->str); + tmp->str=NULL; + } + } + if (tmp->longdesc!=NULL) { + free(tmp->longdesc); + tmp->longdesc=NULL; + } + if (tmp!=NULL) { + free(tmp); + tmp=NULL; + } + i++; + } while (cur!=NULL); + + return i; +} + + + +// Deletes all elements except the specified element which us usually +// the head element. Also 'used' elements will be removed! +// +void automops_delete_all (struct automops *list) +{ + struct automops *head = list; + struct automops *cur = list->next; + struct automops *tmp; + + // Delete all but head element: + while (head != cur) + { + tmp = cur->next; + if (verbose) { + fprintf(stderr, " Deleting '%s'\n",cur->name); + } + automops_delete_protocol(cur); + cur = tmp; + } + head->next = head; + head->prev = head; + + if (verbose) { + fprintf(stderr, " Deleting '%s'\n",head->name); + } + + if (head->payload_s) { + if (head->payload!=NULL) { + free (head->payload); + head->payload=NULL; + } + } + automops_set_defaults(head); +} + + +// Completely clean up. +// After that, there is no automops list anymore. +// You only need this function when stopping mausezahn. +// +void automops_cleanup (struct automops *list) +{ + // 1) delete all elements except head: + automops_delete_all(list); + + // 2) delete head: + automops_delete_fields (list); + if (list->payload_s) { + if (list->payload!=NULL) { + free (list->payload); + list->payload=NULL; + } + } + if (list!=NULL) { + free(list); + list=NULL; + } +} + +// Converts amperr error values in 'e' to string messages 's' +// which must be at least 64 bytes in size. +// +// RETURN VALUE: 0 if convertable, 1 else +// +int amperr2str (int e, char *s) +{ + switch (e) { + + case ampSuccess: + break; + case ampInvalidIndex: + mz_strncpy(s, "invalid index", 64); + break; + case ampInvalidName: + mz_strncpy(s, "invalid name", 64); + break; + + case ampDuplicateName: + mz_strncpy(s, "duplicate name", 64); + break; + + case ampDescTooLong: + mz_strncpy(s, "description too long", 64); + break; + + case ampInvalidLayer: + mz_strncpy(s, "invalid layer", 64); + break; + + case ampTCPandUDP: + mz_strncpy(s, "either TCP or UDP", 64); + break; + + + case ampInvalidType: + mz_strncpy(s, "invalid type", 64); + break; + + case ampUnknownKeyword: + mz_strncpy(s, "unknown keyword", 64); + break; + + case ampSingleWordRequired: + mz_strncpy(s, "single word required", 64); + break; + + case ampRangeError: + mz_strncpy(s, "invalid range", 64); + break; + + case ampPayloadType: + mz_strncpy(s, "invalid payload type", 64); + break; + + case ampPayloadLen: + mz_strncpy(s, "payload length exceeded", 64); + break; + + + case ampUnknownTag: + mz_strncpy(s, "unknown tag (check mausezahn version?)", 64); + break; + + default: + mz_strncpy(s, "completely unknown cause", 64); + return 1; + } + return 0; +} + + + + +// Open amp file (containing XML data describing one or more protocols for automops) +// and copy the data into a char array. +// +// NOTE that the char array must be free'd by the caller. +// +// RETURN VALUE: - pointer to char array with the XML data +// - NULL upon failure +// +char * mapfile (char *fn) +{ + int i, c; + long fn_s; + FILE *fd; + char *blob; + + fd = fopen (fn, "r"); + if (fd==NULL) return NULL; + + // Determine length of file + (void) fseek(fd, 0L, SEEK_END); + fn_s = ftell(fd); + if (fn_s > AUTOMOPS_MAX_FILE_SIZE) { + fprintf(stderr, " File '%s' exceeds max allowed length (%lu>%i)\n", + fn, fn_s, AUTOMOPS_MAX_FILE_SIZE); + fclose(fd); + return NULL; + } + if (verbose) fprintf(stderr, " Parsing %lu bytes from '%s'...\n", fn_s, fn); + rewind(fd); + + blob = (char*) malloc(fn_s+1); + if (blob==NULL) { + fclose(fd); + return NULL; + } + + i=0; + while ((c=fgetc(fd)) != EOF) { + blob[i]=(char) c; + i++; + if (i>fn_s) { + fprintf(stderr, " WARNING: parsing '%s' exceeded EOF\n", fn); + break; // should not reach here + } + } + fclose(fd); + blob[i]='\0'; + return blob; +} + + + +// Create automops PDU within *mp based on data in *amp +// +int automops_update (struct mops *mp, struct automops *amp) +{ + + return 0; +} + diff --git a/staging/cdp.c b/staging/cdp.c new file mode 100644 index 0000000..d198d96 --- /dev/null +++ b/staging/cdp.c @@ -0,0 +1,769 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008 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 + * +*/ + +///////////////////////////////////////////////////////////////////// +// +// Send CDP packets +// +///////////////////////////////////////////////////////////////////// + + +#include "mz.h" +#include "cli.h" + + +#define MZ_CDP_HELP \ + "| CDP type: Send arbitrary CDP packets.\n" \ + "| Note:\n" \ + "| - The Ethernet dst and src MAC addresses can be specified but can be also 'rand'.\n" \ + "| - If dst and src are NOT specified then practical defaults are used (src=own MAC, dst=01:00:0C:CC:CC:CC).\n" \ + "|\n" \ + "| ARGUMENT SYNTAX: -t cdp [arguments]\n" \ + "|\n" \ + "| ARGUMENTS:\n" \ + "|\n" \ + "| version ...... 0-255, default: 2\n" \ + "| ttl ...... 0-255, default: 180 s\n" \ + "| sum ...... 0000-ffff, default: automatically computed\n" \ + "|\n" \ + "| TLVs: Description: Example:\n" \ + "|\n" \ + "| tlv_id ....... Device ID Mausezahn station\n" \ + "| tlv_address ....... Sending interface address 10.1.1.2\n" \ + "| tlv_portid ....... Port Identifier 2/23\n" \ + "| tlv_cap ....... Capabilities (hex<7f) 2a\n" \ + "| tlv_version ....... Software Version ver3.0\n" \ + "| tlv_platform ....... Hardware Platform WS-C6509-E\n" \ + "| tlv_vtpdomain ....... VTP Management Domain MyVTPdomain\n" \ + "| tlv_native ....... Native VLAN number (0-4095) 42\n" \ + "| tlv_duplex ....... Full or half duplex full\n" \ + "| tlv_mgmt ....... Management IP address 192.168.1.2\n" \ + "|\n" \ + "| tlv .......... Create ANY TLV using the format: tlv=<type>/<value>, such as tlv=42/mausezahn\n" \ + "| Note: Currently you must omit spaces within <value>! Use underscore instead.\n" \ + "| tlvhex .......... Create ANY TLV and specify the value in hexformat, such as tlv=42/ca:fe:ba:be\n" \ + "| payload|p .......... Optional additional TLVs or any other bytes specified in hex\n" \ + "|\n" \ + "| When the tlv* arguments are used, the TLV length parameter is automatically set.\n" \ + "|\n" \ + "| The capability flags from MSB to LSB are:\n" \ + "| 0 - Repeater - IGMP - Host - Switch - SrcRouteBrdg - TranspBrdg - Router\n" \ + "|\n" \ + "| Optionally the keyword 'change' will create a different System name TLV every time a CDP\n" \ + "| packet is sent. This can be used to fill up a CDP database with different test values.\n" \ + "| Additionally use the '-a rand' command to use different source MAC addresses.\n" \ + "|\n" \ + "| EXAMPLES:\n" \ + "|\n" \ + "| Announce Device ID 'Espresso3000', Capabilities: Router, native VLAN 301:\n" \ + "| mz eth0 -t cdp \"tlv_id=Espresso3000, tlv_cap=01, tlv_native=301\"\n" \ + "|\n" \ + "| Create another TLV using the payload interface (here voice VLAN 400):\n" \ + "| mz eth0 -t cdp p=00:0e:00:07:01:01:90\n" + + + + + +u_int16_t checksum16 (u_int16_t len, u_int8_t buff[]) +{ + + u_int16_t word16; + u_int32_t sum=0; + u_int16_t i; + + // make 16 bit words out of every two adjacent 8 bit words in the packet and add them up + for (i=0; i<len; i=i+2) + { + word16 =((buff[i]<<8)&0xFF00)+(buff[i+1]&0xFF); + sum = sum + (u_int32_t) word16; + } + + // take only 16 bits out of the 32 bit sum and add up the carries + while (sum>>16) + sum = (sum & 0xFFFF)+(sum >> 16); + + // one's complement the result + sum = ~sum; + + return ((u_int16_t) sum); +} + + +// Creates a TLV and returns the whole length of the TLV +unsigned int create_tlv (u_int16_t type, // The 16-bit TYPE number + u_int8_t *value, // The VALUE as prepared hex-array + unsigned int value_len, // needed because VALUE maybe not \0 terminated + u_int8_t *target) // the RESULT i. e. the complete TLV +{ + unsigned int tlvlen; + u_int8_t *x; + + x = (u_int8_t*) &type; // set TYPE + target[0] = *(x+1); + target[1] = *(x); + + tlvlen = value_len + 4; // set VALUE + x = (u_int8_t*) &tlvlen; + target[2] = *(x+1); + target[3] = *(x); + + target+=4; + memcpy((void*) target, (void*) value, (size_t) value_len); + + return tlvlen; +} + + + + +// NOTE: The Length field indicates the total length, in bytes, of the type, length, and value fields! +// +// Interesting TLVs: +// +// TYPE VALUE +// 0001 Device-ID +// 0002 IP Addresses +// 0003 Port ID such as 2/22 +// 0004 Capabilities (Len=8, consists of flags only: Router, TBrdg, SRBrdgm, Switch, Host, IGMP, Repeater) +// 0005 SW Version +// 0006 Platform +// 0009 VTP Domain +// 000a Native VLAN, e.g. 00:0a 00:06 01:2d identifies native VLAN number 301 (=01:2d) +// 000b Duplex +// 000e VoIP VLAN, e.g. 00:0e 00:07 01 01:90 identifies DATA (=01) and VLAN 400 (=01:90) +// 0012 Trust Bitmap +// 0013 Untrusted Port CoS +// 0014 System Name (!!!) +// 0015 System Object Identifier +// 0016 Management Address (!!!), e.g. 0016 0011(=len 17) 00-00-00-01(=one IP only) 01-01-cc-00-04-90-fe-f8-10(=144.254.248.16) +// 0017 Location +// 001a Unknown (???) +// +// The IP address format is a bit strange as 0016 for example demonstrates... + + + +int send_cdp () +{ + libnet_t *l; + libnet_ptag_t t; + char + errbuf[LIBNET_ERRBUF_SIZE], + argval[1024]; + + u_int8_t + packet[MAX_PAYLOAD_SIZE], // this one will finally contain the whole cdp packet (without LLC/SNAP!) + *x, + value[1024], // USE THIS FOR ANYTHING YOU LIKE !!! + value1[1024], // This one is reserved for some code - Don't use it again! + value2[1024], // This one is reserved for some code - Don't use it again! + tlv[1024], + default_id[15] = "Mausezahn rules", + llcsnap[8]= + { + 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x0c, 0x20, 0x00 + }; + + unsigned int + len=0, + len1=0, + len2=0, + type1, + type2; + + u_int16_t + dummy16=0, + tlv_len=0; + + u_int32_t + next_pbyte=0, // points to the next free byte in tx.cdp_payload + dummy32=0, + packet_s; + + char + pld[2048]; + + + unsigned int i=0, count, delay; + int + eth_src_rand=0, + change_value=0; + long int j=0; + + + if (tx.dot1Q) + { + fprintf(stderr," Note: CDP mode does not support 802.1Q builder.\n"); + exit(1); + } + + if (tx.mpls) + { + fprintf(stderr," Note: CDP mode does not support MPLS builder.\n"); + exit(1); + } + + + if (getarg(tx.arg_string,"help", NULL)==1) + { + if (mz_port) + { + cli_print(gcli, "%s", MZ_CDP_HELP); + return -1; + } + else + { + fprintf(stderr,"\n" + MAUSEZAHN_VERSION + "\n%s", MZ_CDP_HELP); + exit(0); + } + + } + + /////////////////////////////////////////////////////////////////////// + // initial defaults: + if (tx.cdp_ttl==0) tx.cdp_ttl=0xb4; // 180 seconds + + if (tx.cdp_version==0) tx.cdp_version = 0x02; + + // The ID is the only required TLV + // If another function already specified it then it must also set the lenght: + if (tx.cdp_tlv_id_len==0) // not set + { + memcpy((void*) tx.cdp_tlv_id, (void*) default_id, 15); + tx.cdp_tlv_id_len=15; + } + + + + + /////////////////////////////////////////////////////////////////////// + // + // Now check for user arguments: + + + if ( (getarg(tx.arg_string,"version", argval)==1) || (getarg(tx.arg_string,"ver", argval)==1) ) + { + if (str2int(argval)>255) + { + fprintf(stderr," mz/send_cdp: version range exceeded, adjusted to max value.\n"); + tx.cdp_version = 0xff; + } + else + { + tx.cdp_version = (u_int8_t) str2int(argval); + } + } + + + if (getarg(tx.arg_string,"ttl", argval)==1) + { + if (str2int(argval)>255) + { + fprintf(stderr," mz/send_cdp: TTL range exceeded, adjusted to max value.\n"); + tx.cdp_ttl = 0xff; + } + else + { + tx.cdp_ttl = (u_int8_t) str2int(argval); + } + } + + if (getarg(tx.arg_string,"sum", argval)==1) + { + + if (strtol(argval,NULL,16)>65535) + { + fprintf(stderr," mz/send_cdp: checksum range exceeded, adjusted to max value.\n"); + tx.cdp_sum = 0xffff; + } + else + { + tx.cdp_sum = (u_int16_t) strtol(argval,NULL,16); + } + } + + //////// + // + // Provide a basic interface for the most important TLVs: + // + + if (getarg(tx.arg_string,"tlv_id", argval)==1) + { + // simply overwrite current content in tx.cdp_tlv_id + tx.cdp_tlv_id[0] = '\0'; + strncpy((char*) tx.cdp_tlv_id, argval,2048); + tx.cdp_tlv_id_len = strlen ((char*)tx.cdp_tlv_id); + } + + + // + // This is something ugly ;-) + // + + if (getarg(tx.arg_string,"change", NULL)==1) + { + memcpy((void*) tx.cdp_tlv_id, (void*) "Mausezahn 00000000000", 21); + tx.cdp_tlv_id_len=21; + change_value = 1; + } + + + // + // NOW write the ID-TLV; this is the only REQUIRED TLV !!! + // and this TLV should be the FIRST one - that's why we + // write it immediately here now: + // + tlv_len = create_tlv (1, tx.cdp_tlv_id, tx.cdp_tlv_id_len, tlv); + memcpy((void*) tx.cdp_payload+next_pbyte, (void*) tlv, tlv_len); + next_pbyte += tlv_len; + + // + // Now the other TLVs may follow: + // + + // Format: Type=2, Len=17, NrOfAddr=00:00:00:01, Protocol=01:01:cc:00, AddrLen=4, IP_Address + // Example: tlv_address = 192.168.1.10 + // Note: currently only one address supported + if (getarg(tx.arg_string,"tlv_address", argval)==1) + { + dummy32 = str2ip32 (argval); + x = (u_int8_t*) &dummy32; + value[0] = 0x00; // NrOfAddr + value[1] = 0x00; + value[2] = 0x00; + value[3] = 0x01; + + value[4] = 0x01; // Protocol + value[5] = 0x01; + value[6] = 0xcc; + value[7] = 0x00; + + value[8] = 0x04; // AddrLen + + value[9] = *(x+3); + value[10] = *(x+2); + value[11] = *(x+1); + value[12] = *(x); + + tlv_len = create_tlv (2, value, 13, tlv); + memcpy((void*) tx.cdp_payload+next_pbyte, (void*) tlv, tlv_len); + next_pbyte += tlv_len; + } + + + + // Format: Type=3 + // Example: tlv_portid = 2/23 + // Note: + if (getarg(tx.arg_string,"tlv_portid", argval)==1) + { + tlv_len = create_tlv (3, (u_int8_t*) argval, strlen(argval), tlv); + memcpy((void*) tx.cdp_payload+next_pbyte, (void*) tlv, tlv_len); + next_pbyte += tlv_len; + } + + // Format: Type=4 + // Example: "tlv_cap = 2a" (= 0010 1010) + // Flags: MSB=0 - Repeater - IGMP - Host - Switch - SrcRouteBrdg - TranspBrdg - Router(LSB) + if (getarg(tx.arg_string,"tlv_cap", argval)==1) + { + if (strlen(argval)>2) + { + fprintf(stderr," mz/send_cdp: Capability value must be specified as a two-digit hexadecimal value!\n"); + exit(1); + } + else + { + str2hex(argval, value+3, 1020); + if (value[3]>0x7f) + { + fprintf(stderr," mz/send_cdp: Capability value must not exceed 7F(hex)\n"); + exit(1); + } + } + + value[0]=0x00; + value[1]=0x00; + value[2]=0x00; + tlv_len = create_tlv (4, value, 4, tlv); + memcpy((void*) tx.cdp_payload+next_pbyte, (void*) tlv, tlv_len); + next_pbyte += tlv_len; + } + + + // Format: Type=5 + // Example: tlv_version = Mausezahn_version_xyz + // Note: Avoid spaces, use underscore instead + if (getarg(tx.arg_string,"tlv_version", argval)==1) + { + tlv_len = create_tlv (5, (u_int8_t*) argval, strlen(argval), tlv); + memcpy((void*) tx.cdp_payload+next_pbyte, (void*) tlv, tlv_len); + next_pbyte += tlv_len; + } + + + // Format: Type=6 + // Example: tlv_platform = WS-C6509-E + // Note: + if (getarg(tx.arg_string,"tlv_platform", argval)==1) + { + tlv_len = create_tlv (6, (u_int8_t*) argval, strlen(argval), tlv); + memcpy((void*) tx.cdp_payload+next_pbyte, (void*) tlv, tlv_len); + next_pbyte += tlv_len; + } + + // Format: Type=9 + // Example: tlv_vtpdomain = MyVTPdomain + // Note: + if (getarg(tx.arg_string,"tlv_vtpdomain", argval)==1) + { + tlv_len = create_tlv (9, (u_int8_t*) argval, strlen(argval), tlv); + memcpy((void*) tx.cdp_payload+next_pbyte, (void*) tlv, tlv_len); + next_pbyte += tlv_len; + } + + + // Format: Type=10, Len=17 + // Example: tlv_native = 100 + // Note: + if (getarg(tx.arg_string,"tlv_native", argval)==1) + { + dummy16 = (u_int16_t) str2int(argval); + if (dummy16>4095) + { + fprintf(stderr," mz/WARNING: native VLAN value exceeds max value (4095) - hope you know what you do!\n"); + } + + x = (u_int8_t*) &dummy16; + value[0] = *(x+1); + value[1] = *(x); + tlv_len = create_tlv (10, value, 2, tlv); + memcpy((void*) tx.cdp_payload+next_pbyte, (void*) tlv, tlv_len); + next_pbyte += tlv_len; + } + + // Format: Type=11 + // Example: tlv_duplex = full | half + // Note: + if (getarg(tx.arg_string,"tlv_duplex", argval)==1) + { + if (strncmp(argval,"full",10)==0) + { + value[0]=0x01; + } + else if (strncmp(argval,"half",10)==0) + { + value[0]=0x00; + } + else + { + value[0]=(u_int8_t) str2int(argval); + if (!quiet) + { + fprintf(stderr," mz/Warning: Only keywords 'half' or 'full' supported." + " Will interprete input as integer.\n"); + } + + } + + tlv_len = create_tlv (11, value, 1, tlv); + memcpy((void*) tx.cdp_payload+next_pbyte, (void*) tlv, tlv_len); + next_pbyte += tlv_len; + } + + // Format: Type=22, Len=17, NrOfAddr=00:00:00:01, Protocol=01:01:cc:00, AddrLen=4, IP_Address + // Example: tlv_mgmt = 10.1.1.99 + // Note: Same format as tlv_address + if (getarg(tx.arg_string,"tlv_mgmt", argval)==1) + { + dummy32 = str2ip32 (argval); + x = (u_int8_t*) &dummy32; + value[0] = 0x00; // NrOfAddr + value[1] = 0x00; + value[2] = 0x00; + value[3] = 0x01; + + value[4] = 0x01; // Protocol + value[5] = 0x01; + value[6] = 0xcc; + value[7] = 0x00; + + value[8] = 0x04; // AddrLen + + value[9] = *(x+3); + value[10] = *(x+2); + value[11] = *(x+1); + value[12] = *(x); + + tlv_len = create_tlv (22, value, 13, tlv); + memcpy((void*) tx.cdp_payload+next_pbyte, (void*) tlv, tlv_len); + next_pbyte += tlv_len; + + } + + + + // + // Eventually there are two generic TLV interfaces: tlv and tlvhex + // + + if (getarg(tx.arg_string,"tlv", argval)==1) + { + // split in TYPE and VALUE + sscanf(argval, "%u/%s", &type1, value1); + len1 = strlen((const char*) value1); + + } + + if (getarg(tx.arg_string,"tlvhex", argval)==1) + { + // split in TYPE and VALUE + sscanf(argval, "%u/%s", &type2, pld); + len2 = str2hex(pld, value2, 1023); + } + + + // + // Finally the optional payload interface allows to specify subsequent TLVs or any other bytes: + // + if ( (getarg(tx.arg_string,"payload", argval)==1) || (getarg(tx.arg_string,"p", argval)==1)) + { + len = str2hex (argval, value, 1023); + memcpy((void*) tx.cdp_payload+next_pbyte, (void*) value, len); + next_pbyte += len; + } + + + + /////////////////////////////////////////////////////////////// + + + + // Write other TLVs: First the ASCII specified: + if (len1) + { + tlv_len = create_tlv (type1, value1, len1 , tlv); + memcpy((void*) tx.cdp_payload+next_pbyte, (void*) tlv, tlv_len); + next_pbyte += tlv_len; + } + + // Write other TLVs: Then the HEX specified: + if (len2) + { + tlv_len = create_tlv (type2, value2, len2 , tlv); + memcpy((void*) tx.cdp_payload+next_pbyte, (void*) tlv, tlv_len); + next_pbyte += tlv_len; + } + + + tx.cdp_payload_s = next_pbyte; + + // CHECK: + // bs2str(tx.cdp_payload, pld, tx.cdp_payload_s); + // printf("PAYLOAD= %s\n",pld); + + +//////////////////////////// +// + + // Open the link - for the intermediate CDP/LLC frame + l = libnet_init(LIBNET_LINK_ADV, tx.device, errbuf); + + if (l == NULL) + { + fprintf(stderr, "%s", errbuf); + exit(EXIT_FAILURE); + } + + if (check_eth_mac_txt(ETH_DST)) // if '1' then user did not set MAC address (or problem occurred) + { + str2hex("01:00:0C:CC:CC:CC", tx.eth_dst, 6); + } + + if (check_eth_mac_txt(ETH_SRC)) // if '1' then user did not set MAC address (or problem occurred) + { + // own mac per default (see init.c) + } + + count = tx.count; + eth_src_rand = tx.eth_src_rand; + delay = tx.delay; + + // --------------------------------------------------- + // If you want to change CDP fields during a LOOP then + // START the loop from HERE: + // + + //////////////////////////////////// + // Now create the whole CDP packet: + + packet[0] = tx.cdp_version; // VERSION + packet[1] = tx.cdp_ttl; // TTL + packet[2] = 0x00; // CHECKSUM + packet[3] = 0x00; + + // Now add the TLVs + memcpy ((void*) packet+4, (void*) tx.cdp_payload, tx.cdp_payload_s); + packet_s = tx.cdp_payload_s + 4; + + // Check whether packet is an even length (i.e. is a multiple of 16 bits = 2 bytes); + if (packet_s%2>0) + { + packet[packet_s++]=0x00; + packet[packet_s++]=0x17; + packet[packet_s++]=0x00; + packet[packet_s++]=0x05; + packet[packet_s++]=0x00; + } + + + // Now update the checksum: + if (tx.cdp_sum == 0) // Otherwise user specified the checksum (usually a wrong one ;-)) + { + tx.cdp_sum = checksum16(packet_s, packet); + } + x = (u_int8_t *) &tx.cdp_sum; + packet[2] = *(x+1); + packet[3] = *(x); + + // CHECK the CDP packet + //bs2str(packet, pld, packet_s); + //printf("CDP= %s\n",pld); + + +// printf("Len = %u Checksum = %04x \n", packet_s-8, tx.cdp_sum); + + + /////////////////////////////////////////////////////////////// + // Now create the whole tx.eth_payload = LLC/SNAP + CDP packet + // First the LLC/SNAP header: + memcpy ((void*) tx.eth_payload, (void*) llcsnap, 8); + memcpy ((void*) tx.eth_payload+8, (void*) packet, packet_s); + tx.eth_payload_s = packet_s +8; + + + // CHECK the whole 802.3 payload + // bs2str(tx.eth_payload, pld, tx.eth_payload_s); + // printf("PACKET = %s\n",pld); + + + t = libnet_build_802_3 (tx.eth_dst, + tx.eth_src, + tx.eth_payload_s, + tx.eth_payload, + tx.eth_payload_s, + l, + 0); + + + + // this is for the statistics: + mz_start = clock(); + total_d = tx.count; + + if (!count) goto AGAIN; + + for (i=0; i<count; i++) + { + AGAIN: + + if (eth_src_rand) + { + tx.eth_src[0] = (u_int8_t) ( ((float) rand()/RAND_MAX)*256) & 0xFE; // keeps bcast-bit zero + tx.eth_src[1] = (u_int8_t) ( ((float) rand()/RAND_MAX)*256); + tx.eth_src[2] = (u_int8_t) ( ((float) rand()/RAND_MAX)*256); + tx.eth_src[3] = (u_int8_t) ( ((float) rand()/RAND_MAX)*256); + tx.eth_src[4] = (u_int8_t) ( ((float) rand()/RAND_MAX)*256); + tx.eth_src[5] = (u_int8_t) ( ((float) rand()/RAND_MAX)*256); + + t = libnet_build_802_3 (tx.eth_dst, + tx.eth_src, + tx.eth_payload_s, + tx.eth_payload, + tx.eth_payload_s, + l, + t); + + } + + + libnet_write(l); + + if (verbose) + { + bs2str(tx.eth_payload+8, pld, tx.eth_payload_s-8); + fprintf(stderr," Sent CDP: (Ver=%u, TTL=%u) %s\n", tx.cdp_version, tx.cdp_ttl, pld); + } + + if (delay) SLEEP (delay); + + if (change_value) + { + // Note: this only works when default_id has been used + // because otherwise the TLV with the ID might be too short!!! + + // Offset 26-36 contains 00000000000 (of the default id) + // ASCII 0x30-0x39 contain numbers 0-9 + + tx.eth_payload[26] = (u_int8_t) (0x30+ ((float) rand()/RAND_MAX)*10); + tx.eth_payload[27] = (u_int8_t) (0x30+ ((float) rand()/RAND_MAX)*10); + tx.eth_payload[28] = (u_int8_t) (0x30+ ((float) rand()/RAND_MAX)*10); + tx.eth_payload[29] = (u_int8_t) (0x30+ ((float) rand()/RAND_MAX)*10); + tx.eth_payload[30] = (u_int8_t) (0x30+ ((float) rand()/RAND_MAX)*10); + tx.eth_payload[31] = (u_int8_t) (0x30+ ((float) rand()/RAND_MAX)*10); + tx.eth_payload[32] = (u_int8_t) (0x30+ ((float) rand()/RAND_MAX)*10); + tx.eth_payload[33] = (u_int8_t) (0x30+ ((float) rand()/RAND_MAX)*10); + tx.eth_payload[34] = (u_int8_t) (0x30+ ((float) rand()/RAND_MAX)*10); + tx.eth_payload[35] = (u_int8_t) (0x30+ ((float) rand()/RAND_MAX)*10); + tx.eth_payload[36] = (u_int8_t) (0x30+ ((float) rand()/RAND_MAX)*10); + + tx.eth_payload[10] = 0x00; // reset the checksum + tx.eth_payload[11] = 0x00; + tx.cdp_sum = checksum16(tx.eth_payload_s-8, tx.eth_payload+8); + x = (u_int8_t *) &tx.cdp_sum; + tx.eth_payload[10] = *(x+1); + tx.eth_payload[11] = *(x); + + t = libnet_build_802_3 (tx.eth_dst, + tx.eth_src, + tx.eth_payload_s, + tx.eth_payload, + tx.eth_payload_s, + l, + t); + + j++; + + } + + if (!count) goto AGAIN; + } + + + // Destroy contexts + libnet_destroy(l); + + + + return t; + + +} diff --git a/staging/cli.c b/staging/cli.c new file mode 100644 index 0000000..b74d688 --- /dev/null +++ b/staging/cli.c @@ -0,0 +1,570 @@ +/* + * 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 + * +*/ + + + +#include "mz.h" +#include "cli.h" +#include "mops.h" +#include "llist.h" + +void mz_cli_init() +{ + amp_head = automops_init(); + + // Initialize default credentials (will be overwritten by mz.cfg) + strcpy(mz_username, MZ_DEFAULT_USERNAME); + strcpy(mz_password, MZ_DEFAULT_PASSWORD); + strcpy(mz_enable, MZ_DEFAULT_ENABLE_PASSWORD); + + // read login credentials from config file + if (cli_read_cfg("mz.cfg")) { + fprintf(stderr, "mz: Problems opening config file. Will use defaults\n"); + } + + if ((verbose) && (AUTOMOPS_ENABLED)) { + automops_dump_all(amp_head); + fprintf(stderr, "------------ MOPS/CLI initialization completed ------------\n"); + } +} + + + + +// Read in configuration file +int cli_read_cfg(char *str) +{ + char filename[256]; + char line[256]; + char path[256]; + char *ampfile; + char dev[256]; + FILE *fd; + int i, j=0, len, found=0, nonspc=0; + int user=0, pass=0, ena=0, amp=0, mgmt_only=0, cli=0; + + strncpy(filename, str, 255); + + if (getfullpath_cfg(filename)) return 1; + + if (verbose) { + fprintf(stderr, "Opening config file %s...\n", filename); + } + + fd = fopen (filename, "r"); + if (fd==NULL) return 1; + + while (fgets(line, 255, fd) != NULL) { + len=strnlen(line, 255); + // Take string left side of # (comments) + if (len) for(i=0;i<len;i++) if (line[i]=='#') line[i]='\0'; // cut off + len=strnlen(line, 255); + if (len) for(i=0;i<len;i++) if (!isspace(line[i])) nonspc++; + if (nonspc==0) continue; else nonspc=0; + if (!user) user = sscanf(line, " user = %s ", mz_username); + if (!pass) pass = sscanf(line, " password = %s ", mz_password); + if (!ena) ena = sscanf(line, " enable = %s ", mz_enable); + if (!cli) cli = sscanf(line, " cli-device = %s ", dev); + if (cli==1) { + for (i=0; i<device_list_entries; i++) { + if (strncmp(device_list[i].dev, dev, 16)==0) { + device_list[i].cli=1; + found=1; + break; + } + } + if (!found) { + fprintf(stderr, " Warning: [%s] cli device '%s' does not exist!\n", filename, dev); + cli=0; // try again + } + found=0; + cli=0; + } + + if (!mgmt_only) mgmt_only = sscanf(line, " management-only = %s ", dev); + if (mgmt_only==1) { + for (i=0; i<device_list_entries; i++) { + if (strncmp(device_list[i].dev, dev, 16)==0) { + device_list[i].mgmt_only=1; + found=1; + break; + } + } + if (!found) fprintf(stderr, " Warning: [%s] management device '%s' does not exist!\n", filename, dev); + mgmt_only=0; + found=0; + } + + if (AUTOMOPS_ENABLED) { + // read-in all protocol definitions + amp = sscanf(line, " automops = %s ", path); + if (amp) { + ampfile = mapfile(path); + if (ampfile==NULL) fprintf(stderr, " Warning: Cannot read %s\n", path); + else { + j = 0; + j = parse_protocol (ampfile); + if (j) { + if (verbose) { + fprintf(stderr, " Warning: invalid protocol definitions in %s\n", path); + } + } + free(ampfile); + amp=0; + } + } + } + } + fclose(fd); + + if (verbose) { + if (user!=1) + fprintf(stderr, "%s: No user name specified - will use default.\n", filename); + + if (pass!=1) + fprintf(stderr, "%s: No password specified - will use default.\n", filename); + + if (ena!=1) + fprintf(stderr, "%s: No enable password specified - will use default.\n", filename); + } + + cli_debug = 0; + return 0; +} + + + + + +///// TODO *************************************************************** +// +// Process "startup-config" using: +// +// cli_file (struct cli_def *cli, FILE *f, int privilege, int mode) +// +// This reads and processes every line read from f as if it were entered +// at the console. The privilege level will be set to privilege and mode +// set to mode during the processing of the file. +// +// +// Idle timeout or watchdog or whatever: +// +// cli_regular (struct cli_def *cli, int(*callback)(struct cli_def *)) +// +// Adds a callback function which will be called every second that a user +// is connected to the cli. This can be used for regular processing such +// as debugging, time counting or implementing idle timeouts. +// +// Pass NULL as the callback function to disable this at runtime. +// +// ************************************************************************ + + +int cli() +{ + struct sockaddr_in servaddr; + struct cli_command + *address, + *clear, + *debug, + *eth_frame, + *frame, + *ip_packet, + *ip_int, + *launch, + *mac_packet, + *macaddr, + *mac_int, + *pld, + *port, + *reset, + *run, + *show, + *tag, + *tcp_packet, + *udp_packet; + + struct cli_def *cli; + int on = 1, x, s, cnt=0; + int i; + char TimeStamp[128]; + + (void) signal(SIGINT, clean_up); // to close and free everything upon SIGINT + + // Must be called first to setup data structures + cli = cli_init(); + gcli = cli; + + // Set the hostname (shown in the the prompt) + cli_set_hostname(cli, MZ_PROMPT); + + // Set the greeting + cli_set_banner(cli, "mausezahn " VERSION_STRING); + + // Enable usernames and passwords + cli_allow_user(cli, mz_username, mz_password); + cli_allow_enable(cli, mz_enable); + + // Initialize MOPS + mp_head = mops_init(); // now mp_head points to the head of the doubly linked list + + // Initialize packet sequences list + packet_sequences = mz_ll_create_new_element(NULL); + + + mops_rx_arp(); + lookupdev(); + for (i=0; i<device_list_entries; i++) { + get_dev_params(device_list[i].dev); + } + + // Initialize sequence list + + + // **************** THE MAIN CLI COMMANDS **************** + + // ---- DEBUG MODE: ---- + debug = cli_register_command(cli, NULL, "debug", NULL, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Enter debug mode"); + cli_register_command(cli, debug, "packet", debug_packet, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Debug packet processing"); + cli_register_command(cli, debug, "all", debug_all, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Debug all (beware!)"); + + // ---- INTERFACE MODE COMMANDS: ---- (these are defaults for the 'device defaults' command) + cli_register_command(cli, NULL, "interface", enter_interface, PRIVILEGE_PRIVILEGED, MODE_CONFIG, "Enter interface configuration mode"); + ip_int = cli_register_command(cli, NULL, "ip", NULL, PRIVILEGE_PRIVILEGED, MZ_MODE_INTERFACE, "Configure interface IP address"); + cli_register_command(cli, ip_int, "address", conf_ip_address, PRIVILEGE_PRIVILEGED, MZ_MODE_INTERFACE, "Configure interface IP address"); + mac_int= cli_register_command(cli, NULL, "mac", NULL, PRIVILEGE_PRIVILEGED, MZ_MODE_INTERFACE, "Configure interface MAC address"); + cli_register_command(cli, mac_int, "address", conf_mac_address, PRIVILEGE_PRIVILEGED, MZ_MODE_INTERFACE, "Configure interface MAC address"); + tag = cli_register_command(cli, NULL, "tag", NULL, PRIVILEGE_PRIVILEGED, MZ_MODE_INTERFACE, "Configure tags"); + cli_register_command(cli, tag, "dot1q", conf_tag_dot1q, PRIVILEGE_PRIVILEGED, MZ_MODE_INTERFACE, "Configure 802.1Q and 802.1P parameters"); + cli_register_command(cli, tag, "mpls", conf_tag_mpls, PRIVILEGE_PRIVILEGED, MZ_MODE_INTERFACE, "Configure mpls label stack"); + + // ---- VARIOUS CONFIG MODE COMMANDS : ---- + frame = cli_register_command(cli, NULL, "frame", NULL, PRIVILEGE_PRIVILEGED, MODE_CONFIG, "Configure global frame settings"); + cli_register_command(cli, frame, "limit", conf_frame_limit, PRIVILEGE_PRIVILEGED, MODE_CONFIG, "Configure frame size limits"); + cli_register_command(cli, NULL, "sequence", conf_sequence, PRIVILEGE_PRIVILEGED, MODE_CONFIG, "Configure a sequence of packets"); + + // ---- PACKET CONFIG MODE COMMANDS: ---- + cli_register_command(cli, NULL, "packet", enter_packet, PRIVILEGE_PRIVILEGED, MODE_CONFIG, "Enter packet configuration mode"); + cli_register_command(cli, NULL, "clone", cmd_packet_clone, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Clone from another packet"); + cli_register_command(cli, NULL, "name", cmd_packet_name, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Assign a unique name"); + cli_register_command(cli, NULL, "description", cmd_packet_description, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Assign a packet description text"); + cli_register_command(cli, NULL, "bind", cmd_packet_bind, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Select the network interface"); + cli_register_command(cli, NULL, "count", cmd_packet_count, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Configure the packet count value"); + cli_register_command(cli, NULL, "delay", cmd_packet_delay, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Configure the inter-packet delay"); + cli_register_command(cli, NULL, "interval", cmd_packet_interval, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Configure a greater interval"); + cli_register_command(cli, NULL, "type", cmd_packet_type, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Specify packet type"); + mac_packet = cli_register_command(cli, NULL, "mac", NULL, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Configure packet's MAC addresses"); + address = cli_register_command(cli, mac_packet, "address", NULL, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Configure packet's source IP address"); + cli_register_command(cli, address, "source", cmd_packet_mac_address_source, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Configure packet's source MAC addresses"); + cli_register_command(cli, address, "destination", cmd_packet_mac_address_destination, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Configure packet's destination MAC addresses"); + tag = cli_register_command(cli, NULL, "tag", NULL, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Configure tags"); + cli_register_command(cli, tag, "dot1q", cmd_packet_dot1q, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Configure 802.1Q (and 802.1P) parameters"); + cli_register_command(cli, tag, "mpls", cmd_packet_mpls, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Configure MPLS label stack"); + pld = cli_register_command(cli, NULL, "payload", NULL, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Configure a payload"); + cli_register_command(cli, pld, "hex", cmd_packet_payload_hex, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Configure a payload in hexadecimal format"); + cli_register_command(cli, pld, "ascii", cmd_packet_payload_ascii, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Configure a payload in ascii format"); + cli_register_command(cli, pld, "raw", cmd_packet_payload_raw, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Configure a raw payload (whole file as it is)"); + port = cli_register_command(cli, NULL, "port", NULL, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Configure packet's port numbers"); + cli_register_command(cli, port, "source", cmd_port_source, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Configure packet's source port number"); + cli_register_command(cli, port, "destination", cmd_port_destination, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Configure packet's destination port number"); + cli_register_command(cli, NULL, "end", cmd_packet_end, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "End packet configuration mode"); + + // ---------- Ethernet related (for all packets that have Ethernet or LLC/SNAP as link layer) + eth_frame = cli_register_command(cli, NULL, "ethernet", NULL, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Configure frame's Ethernet, 802.2, 802.3, or SNAP settings"); + macaddr = cli_register_command(cli, eth_frame, "address", NULL, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Configure frame's source or destination MAC address"); + cli_register_command(cli, macaddr, "source", cmd_packet_mac_address_source, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Configure frame's source MAC addresses"); + cli_register_command(cli, macaddr, "destination", cmd_packet_mac_address_destination, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Configure frame's destination MAC addresses"); + cli_register_command(cli, eth_frame, "type", cmd_eth_type, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Configure Ethernet's type field"); + cli_register_command(cli, eth_frame, "length", cmd_eth_length, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Configure IEEE 802.3 length field"); + cli_register_command(cli, eth_frame, "llc", cmd_eth_llc, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Configure the IEEE 802.2 field"); + cli_register_command(cli, eth_frame, "snap", cmd_eth_snap, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Configure the IEEE 802.2 field"); + + // ---------- IP related (for all packets that have IPv4 as network layer) + ip_packet = cli_register_command(cli, NULL, "ip", NULL, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Configure packet's IP settings"); + address = cli_register_command(cli, ip_packet, "address", NULL, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Configure packet's source or destination IP address"); + cli_register_command(cli, address, "source", cmd_ip_address_source, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Configure packet's source IP address"); + cli_register_command(cli, address, "destination", cmd_ip_address_destination, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Configure packet's destination IP address"); + cli_register_command(cli, ip_packet, "version", cmd_ip_version, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Configure version field in IPv4 header"); + cli_register_command(cli, ip_packet, "ttl", cmd_ip_ttl, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Configure TTL field in IPv4 header"); + cli_register_command(cli, ip_packet, "protocol", cmd_ip_protocol, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Configure protocol field in IPv4 header"); + cli_register_command(cli, ip_packet, "hlen", cmd_ip_hlen, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Configure header-length (aka IHL) field in IPv4 header"); + cli_register_command(cli, ip_packet, "length", cmd_ip_len, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Configure length field in IPv4 header"); + cli_register_command(cli, ip_packet, "identification", cmd_ip_id, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Configure identification field in IPv4 header"); + cli_register_command(cli, ip_packet, "offset", cmd_ip_offset, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Configure fragment offset field in IPv4 header"); + cli_register_command(cli, ip_packet, "checksum", cmd_ip_sum, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Configure checksum field in IPv4 header"); + cli_register_command(cli, ip_packet, "tos", cmd_ip_tos, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Configure type-of-service (ToS) field in IPv4 header"); + cli_register_command(cli, ip_packet, "dscp", cmd_ip_dscp, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Configure the ToS as DSCP field in IPv4 header"); + cli_register_command(cli, ip_packet, "reserved", cmd_ip_rsv, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Configure the reserved flag in IPv4 header"); + cli_register_command(cli, ip_packet, "dont-fragment", cmd_ip_df, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Configure the don't fragment flag in IPv4 header"); + cli_register_command(cli, ip_packet, "more-fragments", cmd_ip_mf, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Configure the more fragments flag in IPv4 header"); + cli_register_command(cli, ip_packet, "fragment-size", cmd_ip_fragsize, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Configure the fragment size to enable fragmentation"); + cli_register_command(cli, ip_packet, "fragment-overlap", cmd_ip_fragoverlap, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Configure a fragmentation overlap"); + cli_register_command(cli, ip_packet, "option", cmd_ip_option, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Configure IPv4 options"); + cli_register_command(cli, ip_packet, "auto-delivery", cmd_ip_delivery, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Enable or disable IP auto-delivery"); + // --------- IP commands: + cli_register_command(cli, NULL, "version", cmd_ip_version, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_IP, "Specify the IP version (default: 4)"); + cli_register_command(cli, NULL, "ttl", cmd_ip_ttl, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_IP, "Specify the TTL (default: 255)"); + cli_register_command(cli, NULL, "source-address", cmd_ip_address_source, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_IP, "Specify the source IP address"); + cli_register_command(cli, NULL, "destination-address", cmd_ip_address_destination, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_IP, "Specify the destination IP address"); + cli_register_command(cli, NULL, "protocol", cmd_ip_protocol, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_IP, "Specify the IP protocol"); + cli_register_command(cli, NULL, "hlen", cmd_ip_hlen, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_IP, "Specify the IP header length"); + cli_register_command(cli, NULL, "len", cmd_ip_len, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_IP, "Specify the IP packet length"); + cli_register_command(cli, NULL, "identification", cmd_ip_id, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_IP, "Specify the IP identification"); + cli_register_command(cli, NULL, "offset", cmd_ip_offset, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_IP, "Specify the fragment offset"); + cli_register_command(cli, NULL, "sum", cmd_ip_sum, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_IP, "Specify the IP header checksum"); + cli_register_command(cli, NULL, "tos", cmd_ip_tos, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_IP, "Specify the Type of Service"); + cli_register_command(cli, NULL, "dscp", cmd_ip_dscp, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_IP, "Specify the DSCP"); + cli_register_command(cli, NULL, "reserved", cmd_ip_rsv, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_IP, "Set or unset the reserved bit"); + cli_register_command(cli, NULL, "df", cmd_ip_df, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_IP, "Set or unset the Don't Fragment (DF) bit"); + cli_register_command(cli, NULL, "mf", cmd_ip_mf, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_IP, "Set or unset the More Fragments (MF) bit"); + cli_register_command(cli, NULL, "fragment-size", cmd_ip_fragsize, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_IP, "Configure the fragment size to enable fragmentation"); + cli_register_command(cli, NULL, "fragment-overlap", cmd_ip_fragoverlap, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_IP, "Configure a fragmentation overlap"); + cli_register_command(cli, NULL, "option", cmd_ip_option, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_IP, "Configure an IP option"); + cli_register_command(cli, NULL, "auto-delivery", cmd_ip_delivery, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_IP, "Enable or disable IP auto-delivery"); + cli_register_command(cli, NULL, "end", cmd_ip_end, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_IP, "End IP configuration mode"); + + // ---------- UDP related (for all packets that have UDP as transport layer) + udp_packet = cli_register_command(cli, NULL, "udp", NULL, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Configure packet's UDP header parameters"); + cli_register_command(cli, udp_packet, "checksum", cmd_udp_sum, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Configure the UDP checksum"); + cli_register_command(cli, udp_packet, "length", cmd_udp_len, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Configure the UDP length field"); + // ---------- UDP commands: + cli_register_command(cli, NULL, "checksum", cmd_udp_sum, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_UDP, "Configure the UDP checksum"); + cli_register_command(cli, NULL, "length", cmd_udp_len, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_UDP, "Configure the UDP length field"); + cli_register_command(cli, NULL, "end", cmd_udp_end, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_UDP, "End UDP configuration mode"); + + // ---------- TCP related (for all packets that have TCP as transport layer) + tcp_packet = cli_register_command(cli, NULL, "tcp", NULL, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Configure packet's TCP header parameters"); + cli_register_command(cli, tcp_packet, "seqnr", cmd_tcp_seqnr, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Configure the TCP sequence number"); + cli_register_command(cli, tcp_packet, "acknr", cmd_tcp_acknr, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Configure the TCP acknowledgement number"); + cli_register_command(cli, tcp_packet, "hlen", cmd_tcp_offset, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Configure the TCP header length"); + cli_register_command(cli, tcp_packet, "reserved", cmd_tcp_res, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Configure the TCP reserved field"); + cli_register_command(cli, tcp_packet, "flags", cmd_tcp_flags, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Configure a combination of TCP flags at once"); + cli_register_command(cli, tcp_packet, "cwr", cmd_tcp_cwr, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Set or unset the TCP CWR flag"); + cli_register_command(cli, tcp_packet, "ece", cmd_tcp_ece, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Set or unset the TCP ECE flag"); + cli_register_command(cli, tcp_packet, "urg", cmd_tcp_urg, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Set or unset the TCP URG flag"); + cli_register_command(cli, tcp_packet, "ack", cmd_tcp_ack, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "set or unset the TCP ACK flag"); + cli_register_command(cli, tcp_packet, "psh", cmd_tcp_psh, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "set or unset the TCP PSH flag"); + cli_register_command(cli, tcp_packet, "rst", cmd_tcp_rst, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "set or unset the TCP RST flag"); + cli_register_command(cli, tcp_packet, "syn", cmd_tcp_syn, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "set or unset the TCP SYN flag"); + cli_register_command(cli, tcp_packet, "fin", cmd_tcp_fin, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "set or unset the TCP FIN flag"); + cli_register_command(cli, tcp_packet, "window", cmd_tcp_window, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Configure the TCP window size"); + cli_register_command(cli, tcp_packet, "checksum", cmd_tcp_sum, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Configure the TCP checksum"); + cli_register_command(cli, tcp_packet, "urgent-pointer", cmd_tcp_urgptr, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Configure the TCP urgend pointer"); + cli_register_command(cli, tcp_packet, "options", cmd_tcp_options, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET, "Configure TCP options"); + // ---------- TCP commands: + cli_register_command(cli, NULL, "seqnr", cmd_tcp_seqnr, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_TCP, "Configure the TCP sequence number"); + cli_register_command(cli, NULL, "acknr", cmd_tcp_acknr, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_TCP, "Configure the TCP acknowledgement number"); + cli_register_command(cli, NULL, "hlen", cmd_tcp_offset, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_TCP, "Configure the TCP header length"); + cli_register_command(cli, NULL, "reserved", cmd_tcp_res, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_TCP, "Configure the TCP reserved field"); + cli_register_command(cli, NULL, "flags", cmd_tcp_flags, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_TCP, "Configure a combination of TCP flags at once"); + cli_register_command(cli, NULL, "cwr", cmd_tcp_cwr, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_TCP, "Set or unset the TCP CWR flag"); + cli_register_command(cli, NULL, "ece", cmd_tcp_ece, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_TCP, "Set or unset the TCP ECE flag"); + cli_register_command(cli, NULL, "urg", cmd_tcp_urg, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_TCP, "Set or unset the TCP URG flag"); + cli_register_command(cli, NULL, "ack", cmd_tcp_ack, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_TCP, "set or unset the TCP ACK flag"); + cli_register_command(cli, NULL, "psh", cmd_tcp_psh, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_TCP, "set or unset the TCP PSH flag"); + cli_register_command(cli, NULL, "rst", cmd_tcp_rst, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_TCP, "set or unset the TCP RST flag"); + cli_register_command(cli, NULL, "syn", cmd_tcp_syn, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_TCP, "set or unset the TCP SYN flag"); + cli_register_command(cli, NULL, "fin", cmd_tcp_fin, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_TCP, "set or unset the TCP FIN flag"); + cli_register_command(cli, NULL, "window", cmd_tcp_window, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_TCP, "Configure the TCP window size"); + cli_register_command(cli, NULL, "checksum", cmd_tcp_sum, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_TCP, "Configure the TCP checksum"); + cli_register_command(cli, NULL, "urgent-pointer", cmd_tcp_urgptr, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_TCP, "Configure the TCP urgend pointer"); + cli_register_command(cli, NULL, "options", cmd_tcp_options, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_TCP, "Configure TCP options"); + cli_register_command(cli, NULL, "end", cmd_tcp_end, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_TCP, "End TCP configuration mode"); + + // --------- ARP commands: + cli_register_command(cli, NULL, "hardware-type", cmd_arp_hwtype, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_ARP, "Specify the hardware type"); + cli_register_command(cli, NULL, "protocol-type", cmd_arp_prtype, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_ARP, "Specify the protocol type"); + cli_register_command(cli, NULL, "hw-addr-size", cmd_arp_hwaddrsize, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_ARP, "Specify the hardware address size"); + cli_register_command(cli, NULL, "pr-addr-size", cmd_arp_praddrsize, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_ARP, "Specify the protocol address size"); + cli_register_command(cli, NULL, "opcode", cmd_arp_opcode, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_ARP, "Specify the ARP opcode"); + cli_register_command(cli, NULL, "sender-mac", cmd_arp_smac, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_ARP, "Specify the sender MAC address"); + cli_register_command(cli, NULL, "sender-ip", cmd_arp_sip, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_ARP, "Specify the sender IP address"); + cli_register_command(cli, NULL, "target-mac", cmd_arp_tmac, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_ARP, "Specify the target MAC address"); + cli_register_command(cli, NULL, "target-ip", cmd_arp_tip, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_ARP, "Specify the target IP address"); + cli_register_command(cli, NULL, "trailer", cmd_arp_trailer, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_ARP, "Specify the trailer length"); + cli_register_command(cli, NULL, "end", cmd_arp_end, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_ARP, "End ARP configuration mode"); + + // --------- BPDU commands: + cli_register_command(cli, NULL, "id", cmd_bpdu_id, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_BPDU, "Specify the BPDU identifier"); + cli_register_command(cli, NULL, "version", cmd_bpdu_version, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_BPDU, "Specify the BPDU version"); + cli_register_command(cli, NULL, "bpdutype", cmd_bpdu_type, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_BPDU, "Specify the BPDU type"); + cli_register_command(cli, NULL, "flags", cmd_bpdu_flags, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_BPDU, "Specify the BPDU flags"); + cli_register_command(cli, NULL, "root-id", cmd_bpdu_rid, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_BPDU, "Specify the BPDU root identifier"); + cli_register_command(cli, NULL, "path-cost", cmd_bpdu_pc, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_BPDU, "Specify the BPDU root path cost"); + cli_register_command(cli, NULL, "bridge-id", cmd_bpdu_bid, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_BPDU, "Specify the BPDU bridge identifier"); + cli_register_command(cli, NULL, "port-id", cmd_bpdu_pid, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_BPDU, "Specify the BPDU port identifier"); + cli_register_command(cli, NULL, "age", cmd_bpdu_age, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_BPDU, "Specify the BPDU age"); + cli_register_command(cli, NULL, "maxage", cmd_bpdu_maxage, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_BPDU, "Specify the BPDU maxage"); + cli_register_command(cli, NULL, "hello-interval", cmd_bpdu_hello, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_BPDU, "Specify the BPDU hello interval"); + cli_register_command(cli, NULL, "forward-delay", cmd_bpdu_fwd, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_BPDU, "Specify the BPDU forward delay"); + cli_register_command(cli, NULL, "mode", cmd_bpdu_mode, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_BPDU, "Specify the BPDU mode"); + cli_register_command(cli, NULL, "vlan", cmd_bpdu_vlan, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_BPDU, "Specify the vlan for PVST+"); + cli_register_command(cli, NULL, "end", cmd_bpdu_end, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_BPDU, "End BPDU configuration mode"); + + // --------- IGMP commands: + cli_register_command(cli, NULL, "v2-general-query", cmd_igmpv2_genquery, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_IGMP, "Create an IGMPv2 general query"); + cli_register_command(cli, NULL, "v2-group-specific-query", cmd_igmpv2_specquery, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_IGMP, "Create an IGMPv2 group-specific query"); + cli_register_command(cli, NULL, "v2-report", cmd_igmpv2_report, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_IGMP, "Create an IGMPv2 membership report"); + cli_register_command(cli, NULL, "v2-leave", cmd_igmpv2_leave, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_IGMP, "Create an IGMPv2 leave group message"); + cli_register_command(cli, NULL, "v1-query", cmd_igmpv1_query, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_IGMP, "Create an IGMPv1 query"); + cli_register_command(cli, NULL, "v1-report", cmd_igmpv1_report, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_IGMP, "Create an IGMPv1 membership report"); + cli_register_command(cli, NULL, "end", cmd_ip_end, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_IGMP, "End IGMP configuration mode"); // we reuse cmd_ip_end here! + + cli_register_command(cli, NULL, "conformance", cmd_lldp_conformance, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_LLDP, "Enable or disable LLDP standard conformance"); + cli_register_command(cli, NULL, "chassis-id", cmd_lldp_chassis_id, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_LLDP, "Configure the LLDP Chassis-ID"); + cli_register_command(cli, NULL, "port-id", cmd_lldp_port_id, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_LLDP, "Configure the LLDP Port-ID"); + cli_register_command(cli, NULL, "ttl", cmd_lldp_ttl, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_LLDP, "Configure the LLDP Time-to-Live"); + cli_register_command(cli, NULL, "vlan", cmd_lldp_vlan, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_LLDP, "Configure the LLDP Port VLAN-ID"); + cli_register_command(cli, NULL, "generic-tlv", cmd_lldp_opt_tlv, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_LLDP, "Configure a generic LLDP TLV"); + cli_register_command(cli, NULL, "bad-tlv", cmd_lldp_opt_tlv_bad, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_LLDP, "Configure a bad TLV for testing purposes"); + cli_register_command(cli, NULL, "organisational-tlv", cmd_lldp_opt_org, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_LLDP, "Configure an organisational LLDP TLV"); + cli_register_command(cli, NULL, "early-end", cmd_lldp_endtlv, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_LLDP, "Insert an 'early' End-of-LLDPU TLV"); + cli_register_command(cli, NULL, "reset", cmd_lldp_reset, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_LLDP, "Reset the LLDPU to defaults and clear all optional TLVs"); + cli_register_command(cli, NULL, "end", cmd_ip_end, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_LLDP, "End IGMP configuration mode"); // we reuse cmd_ip_end here! + + // --------- RTP commands: + cli_register_command(cli, NULL, "version", cmd_rtp_version, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_RTP, "Specify the RTP version (default: 2)"); + cli_register_command(cli, NULL, "padding", cmd_rtp_padding, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_RTP, "Set or unset the padding flag (default: 0)"); + cli_register_command(cli, NULL, "xten", cmd_rtp_xten, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_RTP, "Set or unset the eXtension flag (default: 0)"); + cli_register_command(cli, NULL, "marker", cmd_rtp_marker, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_RTP, "Set or unset the marker flag (default: 0)"); + cli_register_command(cli, NULL, "csrc-count", cmd_rtp_cc, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_RTP, "Configure the CSRC count (default: 0)"); + cli_register_command(cli, NULL, "csrc-list", cmd_rtp_cclist, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_RTP, "Configure the CSRC list (default: none)"); + cli_register_command(cli, NULL, "payload-type", cmd_rtp_pt, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_RTP, "Configure the payload type (default: G.711, A-law, 20 msec)"); + cli_register_command(cli, NULL, "sequence-number", cmd_rtp_sqnr, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_RTP, "Configure the sequence number"); + cli_register_command(cli, NULL, "timestamp", cmd_rtp_time, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_RTP, "Configure the timestamp"); + cli_register_command(cli, NULL, "ssrc", cmd_rtp_ssrc, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_RTP, "Configure the SSRC (source identifier)"); + cli_register_command(cli, NULL, "extension", cmd_rtp_extension, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_RTP, "Configure an extension header"); + cli_register_command(cli, NULL, "source", cmd_rtp_source, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_RTP, "Specify a media source"); + cli_register_command(cli, NULL, "end", cmd_ip_end, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_RTP, "End RTP configuration mode"); // we reuse cmd_ip_end here! + + // --------- DNS commands: + cli_register_command(cli, NULL, "ttl", cmd_dns_ttl, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_DNS, "Specify the TTL (default: 0)"); + cli_register_command(cli, NULL, "query", cmd_dns_query, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_DNS, "Specify the query"); + cli_register_command(cli, NULL, "answer", cmd_dns_answer, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_DNS, "Specify the answer"); + cli_register_command(cli, NULL, "end", cmd_dns_end, PRIVILEGE_PRIVILEGED, MZ_MODE_PACKET_DNS, "End DNS configuration mode"); + + + // --------- SEQUENCE COMMANDS + cli_register_command(cli, NULL, "add", sequence_add, PRIVILEGE_PRIVILEGED, MZ_MODE_SEQUENCE, "Add another packet to the current sequence"); + cli_register_command(cli, NULL, "delay", sequence_delay, PRIVILEGE_PRIVILEGED, MZ_MODE_SEQUENCE, "Add a delay to the current sequence"); + cli_register_command(cli, NULL, "show", sequence_show, PRIVILEGE_PRIVILEGED, MZ_MODE_SEQUENCE, "Show current sequence list"); + cli_register_command(cli, NULL, "remove", sequence_remove, PRIVILEGE_PRIVILEGED, MZ_MODE_SEQUENCE, "Remove a packet or delay from the current sequence"); + cli_register_command(cli, NULL, "end", cmd_end_to_config, PRIVILEGE_PRIVILEGED, MZ_MODE_SEQUENCE, "End sequence configuration mode"); + // ---- BENCHMARK CONFIG MODE COMMANDS: --- + // ---- SCAN CONFIG MODE COMMANDS: --- + + // ---- CONTROL COMMANDS: ---- + cli_register_command(cli, NULL, "terminate", stop_mausezahn, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Terminate the Mausezahn server"); + run = cli_register_command(cli, NULL, "run", NULL, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Run previously configured mops instances or sequences"); + cli_register_command(cli, run, "id", cmd_run_id, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Run mops packet(s) by specifying packet identifiers"); + cli_register_command(cli, run, "name", cmd_run_name, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Run mops packet(s) by specifying packet names"); + cli_register_command(cli, run, "sequence", cmd_run_sequence, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Run a packet sequence"); + cli_register_command(cli, run, "all", cmd_run_all, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Run all currently configured mops packet(s)"); + cli_register_command(cli, NULL, "tx", transmit, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Send inline configured packet (legacy mode; not recommended)"); + cli_register_command(cli, NULL, "stop", cmd_stop, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Stop transmission"); + cli_register_command(cli, NULL, "load", cmd_load, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Load commands from a file"); + + // ---- SET COMMANDS: ----- + cli_register_command(cli, NULL, "set", cmd_set, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Set global Mausezahn parameters"); + + // ---- CLEAR COMMANDS: ----- + clear = cli_register_command(cli, NULL, "clear", NULL, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Clear something (use '?')"); + cli_register_command(cli, clear, "all", clear_all, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Re-initialize Mausezahn"); + cli_register_command(cli, clear, "packet", clear_packet, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Delete a packet (i. e. MOPS entry)"); + + // ---- SHOW COMMANDS: ----- + show = cli_register_command(cli, NULL, "show", NULL, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Show something (use '?')"); + cli_register_command(cli, show, "packet", show_packets, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Show defined packets"); +// cli_register_command(cli, show, "system", show_system, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Show basic system settings"); + cli_register_command(cli, show, "interfaces", show_interfaces, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Show detailed interface information"); + cli_register_command(cli, show, "mops", show_mops, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Show MOPS details"); +// cli_register_command(cli, show, "processes", cmd_test, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "List all Mausezahn processes"); + cli_register_command(cli, show, "set", show_set, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "List general packet parameters"); + cli_register_command(cli, show, "arp", show_arp, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Show the advanced Mausezahn ARP table"); + +// cli_register_command(cli, show, "report", cmd_test, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Print reports"); + + // ---- PRIVILEGE (OTHER) ---- + reset = cli_register_command(cli, NULL, "reset", NULL, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Reset something..."); + cli_register_command(cli, reset, "interface", cmd_reset_interface, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Reset interfaces"); + cli_register_command(cli, reset, "packet", cmd_reset_packet, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Reset interfaces"); + // ------- LAUNCH ------ + launch = cli_register_command(cli, NULL, "launch", NULL, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Launch a predefined MOPS process"); + cli_register_command(cli, launch, "bpdu", launch_bpdu, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Launch a(nother) BPDU process"); + cli_register_command(cli, launch, "synflood", launch_synflood, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Launch a(nother) SYN-Flood process"); +// cli_register_command(cli, launch, "alot", launch_alot, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Launch lots of traffic"); +// cli_register_command(cli, launch, "rtp", launch_rtp, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Launch rtp stream(s)"); +// cli_register_command(cli, launch, "arp", launch_arp, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Launch a(nother) ARP process"); +// cli_register_command(cli, launch, "lldp", launch_lldp, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Launch a(nother) LLDP process"); + + + // ******************************************************* + + // Create a socket + s = socket(AF_INET, SOCK_STREAM, 0); + setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + + // Should we bind the CLI session to a specific interface? + // TODO: This does nothing !? + for (i=0; i<device_list_entries; i++) { + if (device_list[i].cli) { + setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, device_list[i].dev, strnlen(device_list[i].dev, 16)); + break; // can only be one interface + } + } + + + // Listen on port mz_port (default: 25542, towel day) + memset(&servaddr, 0, sizeof(servaddr)); + servaddr.sin_family = AF_INET; + servaddr.sin_addr.s_addr = htonl(INADDR_ANY); // TODO: specified interface + servaddr.sin_port = htons(mz_port); + bind(s, (struct sockaddr *)&servaddr, sizeof(servaddr)); + + // Wait for a connection + listen(s, 50); + + while ((x = accept(s, NULL, 0))) + { + if (!quiet) + { + cnt++; + timestamp_human(TimeStamp, NULL); + fprintf(stderr, "Got incoming connection [%i] at %s.\n", cnt, TimeStamp); + fflush(stderr); + } + + // Pass the connection off to libcli + cli_loop(cli, x); + + if (!quiet) + { + timestamp_human(TimeStamp, NULL); + fprintf(stderr, "Connection [%i] left at %s.\n", cnt, TimeStamp); + } + + close(x); + } + + // Free data structures + cli_done(cli); + + return 0; +} + + diff --git a/staging/cli.h b/staging/cli.h new file mode 100644 index 0000000..6c6cccb --- /dev/null +++ b/staging/cli.h @@ -0,0 +1,268 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008 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 + * +*/ + + + +#ifndef __MAUSEZAHN_CLI__ +#define __MAUSEZAHN_CLI__ + +#include <libcli.h> +#include "mops.h" + +#define CLI_DEBUG_PACKET 0x0001 + +#define MZ_MODE_BENCHMARK 1002 +#define MZ_MODE_SCAN 1003 + +#define MZ_MODE_PACKET 1100 + +#define MZ_MODE_PACKET_ARP 1101 +#define MZ_MODE_PACKET_BPDU 1102 +#define MZ_MODE_PACKET_CDP 1103 +#define MZ_MODE_PACKET_DNS 1104 +#define MZ_MODE_PACKET_IP 1105 +#define MZ_MODE_PACKET_ICMP 1106 +#define MZ_MODE_PACKET_LLDP 1107 +#define MZ_MODE_PACKET_RTP 1108 +#define MZ_MODE_PACKET_SYSLOG 1109 +#define MZ_MODE_PACKET_TCP 1110 +#define MZ_MODE_PACKET_UDP 1111 +#define MZ_MODE_PACKET_ETH 1112 +#define MZ_MODE_PACKET_IGMP 1113 + +#define MZ_MODE_INTERFACE 1200 +#define MZ_MODE_SEQUENCE 1300 + +#define MZ_PROMPT "mz-" MAUSEZAHN_VERSION_SHORT + +#define MZ_DEFAULT_USERNAME "mz" +#define MZ_DEFAULT_PASSWORD "mz" +#define MZ_DEFAULT_ENABLE_PASSWORD "mops" +#define MZ_DEFAULT_PORT 25542 // Towel day and 42 + +struct cli_def *gcli; + +char mz_username[32]; +char mz_password[32]; +char mz_enable[32]; +int mz_port; +struct mops *clipkt; // actual packet used by CLI thread + +int clidev; + +// ================================================================= +int cli_debug; + +// Flags from 0x0000 to 0xFFFF +// cli_debug & 8000 => Developer specific debugs +// cli_debug & 0001 => Packet transmission debugging +// ... + +// ================================================================= + + +/////////////////////////////////////////////////////////////////////////////// +// Prototypes + +void mz_cli_init(); +int cli_read_cfg(char *str); +int mz_def16 (char *def, u_int16_t val, char *str256); +int cli(); + +int debug_all (struct cli_def *cli, const char *command, char *argv[], int argc); +int debug_packet (struct cli_def *cli, const char *command, char *argv[], int argc); + +int cmd_end_to_config(struct cli_def *cli, const char *command, char *argv[], int argc); +int tx_switch(struct cli_def *cli); +int cmd_test(struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_reset_interface (struct cli_def *cli, const char *command, char *argv[], int argc); + +int show_system(struct cli_def *cli, const char *command, char *argv[], int argc); +int show_packets(struct cli_def *cli, const char *command, char *argv[], int argc); +int show_set(struct cli_def *cli, const char *command, char *argv[], int argc); +int show_interfaces(struct cli_def *cli, const char *command, char *argv[], int argc); +int show_mops(struct cli_def *cli, const char *command, char *argv[], int argc); +int show_arp (struct cli_def *cli, const char *command, char *argv[], int argc); + +int cmd_set(struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_run_id (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_run_name (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_run_sequence (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_run_all (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_stop (struct cli_def *cli, const char *command, char *argv[], int argc); + +int launch_bpdu (struct cli_def *cli, const char *command, char *argv[], int argc); +int launch_synflood (struct cli_def *cli, const char *command, char *argv[], int argc); + +int stop_mausezahn(struct cli_def *cli, const char *command, char *argv[], int argc); +int transmit (struct cli_def *cli, const char *command, char *argv[], int argc); +int clear_all(struct cli_def *cli, const char *command, char *argv[], int argc); +int clear_packet(struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_reset_packet(struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_load (struct cli_def *cli, const char *command, char *argv[], int argc); + +int enter_interface (struct cli_def *cli, const char *command, char *argv[], int argc); +int conf_ip_address (struct cli_def *cli, const char *command, char *argv[], int argc); +int conf_mac_address (struct cli_def *cli, const char *command, char *argv[], int argc); +int conf_tag_dot1q (struct cli_def *cli, const char *command, char *argv[], int argc); +int conf_tag_mpls (struct cli_def *cli, const char *command, char *argv[], int argc); + +int conf_frame_limit (struct cli_def *cli, const char *command, char *argv[], int argc); + +int conf_sequence (struct cli_def *cli, const char *command, char *argv[], int argc); +int sequence_add (struct cli_def *cli, const char *command, char *argv[], int argc); +int sequence_delay (struct cli_def *cli, const char *command, char *argv[], int argc); +int sequence_remove (struct cli_def *cli, const char *command, char *argv[], int argc); +int sequence_show (struct cli_def *cli, const char *command, char *argv[], int argc); + + +int enter_packet (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_packet_type(struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_packet_end(struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_packet_clone (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_packet_name (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_packet_description (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_packet_count (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_packet_delay (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_packet_interval (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_packet_bind (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_packet_mac_address_source (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_packet_mac_address_destination (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_eth_type (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_eth_length (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_eth_llc (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_eth_snap (struct cli_def *cli, const char *command, char *argv[], int argc); + +int cmd_packet_dot1q (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_packet_mpls (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_packet_payload_hex (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_packet_payload_ascii (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_packet_payload_raw (struct cli_def *cli, const char *command, char *argv[], int argc); + +int cmd_port_source (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_port_destination (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_udp_sum (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_udp_len (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_udp_end(struct cli_def *cli, const char *command, char *argv[], int argc); + +int cmd_tcp_seqnr (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_tcp_acknr (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_tcp_offset (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_tcp_res (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_tcp_flags (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_tcp_cwr (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_tcp_ece (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_tcp_urg (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_tcp_ack (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_tcp_psh (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_tcp_rst (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_tcp_syn (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_tcp_fin (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_tcp_window (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_tcp_sum (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_tcp_urgptr(struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_tcp_options (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_tcp_end(struct cli_def *cli, const char *command, char *argv[], int argc); + +int cmd_dns_query(struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_dns_answer(struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_dns_ttl(struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_dns_end(struct cli_def *cli, const char *command, char *argv[], int argc); + +int cmd_arp_hwtype (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_arp_prtype (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_arp_hwaddrsize (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_arp_praddrsize (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_arp_opcode (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_arp_smac (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_arp_sip (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_arp_tmac (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_arp_tip (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_arp_trailer (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_arp_end(struct cli_def *cli, const char *command, char *argv[], int argc); + +int cmd_bpdu_id (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_bpdu_version (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_bpdu_type (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_bpdu_flags (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_bpdu_rid (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_bpdu_pc (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_bpdu_bid (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_bpdu_pid (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_bpdu_age (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_bpdu_maxage (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_bpdu_hello (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_bpdu_fwd (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_bpdu_mode (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_bpdu_vlan(struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_bpdu_end(struct cli_def *cli, const char *command, char *argv[], int argc); + +int cmd_igmpv2_genquery (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_igmpv2_specquery (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_igmpv2_report (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_igmpv2_leave (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_igmpv1_query (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_igmpv1_report (struct cli_def *cli, const char *command, char *argv[], int argc); + +int cmd_lldp_conformance (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_lldp_chassis_id (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_lldp_port_id (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_lldp_ttl (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_lldp_vlan (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_lldp_opt_tlv (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_lldp_opt_tlv_bad (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_lldp_opt_org (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_lldp_endtlv (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_lldp_reset (struct cli_def *cli, const char *command, char *argv[], int argc); + +int cmd_ip_address_source (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_ip_address_destination (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_ip_version (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_ip_ttl (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_ip_protocol (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_ip_hlen (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_ip_len (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_ip_id (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_ip_offset (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_ip_sum (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_ip_tos (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_ip_dscp (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_ip_rsv (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_ip_df (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_ip_mf (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_ip_fragsize (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_ip_fragoverlap (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_ip_option (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_ip_delivery (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_ip_end(struct cli_def *cli, const char *command, char *argv[], int argc); + +int cmd_rtp_version (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_rtp_padding (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_rtp_xten (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_rtp_marker (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_rtp_cc (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_rtp_pt (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_rtp_ssrc (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_rtp_sqnr (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_rtp_time (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_rtp_extension (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_rtp_source (struct cli_def *cli, const char *command, char *argv[], int argc); +int cmd_rtp_cclist (struct cli_def *cli, const char *command, char *argv[], int argc); + +#endif + diff --git a/staging/cli_arp.c b/staging/cli_arp.c new file mode 100644 index 0000000..4bf8580 --- /dev/null +++ b/staging/cli_arp.c @@ -0,0 +1,232 @@ +/* + * 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 + * +*/ + + + +#include "mz.h" +#include "cli.h" +#include "mops.h" + + +// ISSUES: +// +// - Currently only IP/MAC resolution supported (i.e. hw_size=6, pr_size=4) +// - Add macro support: commands like request/response should set all params correctly + + + + +int cmd_arp_hwtype (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + struct mops_ext_arp * pd = clipkt->p_desc; + + if ( (strncmp(argv[argc-1],"?",1)==0) || (argc!=1) ) + { + cli_print(cli,"Specify the hardware type (0-ffff, default 1=Eth)\n"); + } + else if (mops_pdesc_2byte(&pd->hw_type, argv[0], 1, 0, 0xffff)) + { + cli_print(cli, "Hardware type must be between 0 and ffff\n"); + } + + return CLI_OK; +} + + + +int cmd_arp_prtype (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + struct mops_ext_arp * pd = clipkt->p_desc; + + if ( (strncmp(argv[argc-1],"?",1)==0) || (argc!=1) ) + { + cli_print(cli,"Specify the protocol type (0-ffff, default=800=IP)\n"); + } + else if (mops_pdesc_2byte(&pd->pr_type, argv[0], 1, 0, 0xffff)) + { + cli_print(cli, "Protocol type must be between 0 and ffff\n"); + } + + return CLI_OK; +} + + + +int cmd_arp_hwaddrsize (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + struct mops_ext_arp * pd = clipkt->p_desc; + + if ( (strncmp(argv[argc-1],"?",1)==0) || (argc!=1) ) + { + cli_print(cli,"Specify the hardware address size (0-255, default=6)\n"); + } + else if (mops_pdesc_1byte(&pd->hw_size, argv[0], 0, 0, 255)) + { + cli_print(cli, "Hardware size must be between 0 and 255\n"); + } + + return CLI_OK; +} + + +int cmd_arp_praddrsize (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + struct mops_ext_arp * pd = clipkt->p_desc; + + if ( (strncmp(argv[argc-1],"?",1)==0) || (argc!=1) ) + { + cli_print(cli,"Specify the protocol address size (0-255, default=4)\n"); + } + else if (mops_pdesc_1byte(&pd->pr_size, argv[0], 0, 0, 255)) + { + cli_print(cli, "Protocol size must be between 0 and 255\n"); + } + + return CLI_OK; +} + + +int cmd_arp_opcode (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + struct mops_ext_arp * pd = clipkt->p_desc; + + if ( (strncmp(argv[argc-1],"?",1)==0) || (argc!=1) ) + { + cli_print(cli,"Specify the ARP operation code (0-ffff)\n"); + cli_print(cli,"Optional keywords: 'request' (default) or 'response'\n"); + } + else if (mz_strcmp(argv[0],"request", 3)==0) + { + cli_print(cli, "Set ARP mode to request\n"); + pd->opcode = 1; + } + else if (mz_strcmp(argv[0],"response", 3)==0) + { + cli_print(cli, "Set ARP mode to response\n"); + pd->opcode = 2; + } + else + { + cli_print(cli, "Invalid ARP mode\n"); + } + + return CLI_OK; +} + + + +int cmd_arp_smac (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + struct mops_ext_arp * pd = clipkt->p_desc; + + if ( (strncmp(argv[argc-1],"?",1)==0) || (argc!=1) ) + { + cli_print(cli,"Specify a source MAC address (format: XX:XX:XX:XX:XX:XX)\n"); + } + else + { + if (mops_pdesc_mac(pd->sender_mac, argv[0])) + { + cli_print(cli,"Invalid MAC address (use format: XX:XX:XX:XX:XX:XX)\n"); + } + } + return CLI_OK; +} + + + +int cmd_arp_sip (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + struct mops_ext_arp * pd = clipkt->p_desc; + + if ( (strncmp(argv[argc-1],"?",1)==0) || (argc!=1) ) + { + cli_print(cli,"Specify a source IP address (format: A.B.C.D)\n"); + } + else if (mops_pdesc_ip (pd->sender_ip, argv[0])) + { + cli_print(cli,"Invalid IP address (use format: A.B.C.D)\n"); + } + + return CLI_OK; +} + + + +int cmd_arp_tmac (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + struct mops_ext_arp * pd = clipkt->p_desc; + + if ( (strncmp(argv[argc-1],"?",1)==0) || (argc!=1) ) + { + cli_print(cli,"Specify a target MAC address (format: XX:XX:XX:XX:XX:XX)\n"); + } + else if (mops_pdesc_mac(pd->target_mac, argv[0])) + { + cli_print(cli,"Invalid MAC address (use format: XX:XX:XX:XX:XX:XX)\n"); + } + + return CLI_OK; +} + + + +int cmd_arp_tip (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + struct mops_ext_arp * pd = clipkt->p_desc; + + if ( (strncmp(argv[argc-1],"?",1)==0) || (argc!=1) ) + { + cli_print(cli,"Specify a target IP address (format: A.B.C.D)\n"); + } + else if (mops_pdesc_ip (pd->target_ip, argv[0])) + { + cli_print(cli,"Invalid IP address (use format: A.B.C.D)\n"); + } + + return CLI_OK; +} + + + +int cmd_arp_trailer (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + struct mops_ext_arp * pd = (MOPS_EXT_ARP) clipkt->p_desc; + + if ( (strncmp(argv[argc-1],"?",1)==0) || (argc!=1) ) + { + cli_print(cli,"Specify the trailer length (0-2000, default=18)\n"); + } + else if (mops_pdesc_2byte(&pd->trailer, argv[0], 0, 0, 2000)) + { + cli_print(cli, "Trailer must be between 0 and 2000\n"); + } + + return CLI_OK; +} + + + +int cmd_arp_end(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + char prompt[16]; + sprintf(prompt, "pkt-%i",clipkt->id); + cli_set_configmode(cli, MZ_MODE_PACKET, prompt); + return CLI_OK; +} + diff --git a/staging/cli_bpdu.c b/staging/cli_bpdu.c new file mode 100644 index 0000000..8cc2a69 --- /dev/null +++ b/staging/cli_bpdu.c @@ -0,0 +1,750 @@ +/* + * 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 + * +*/ + + + +#include "mz.h" +#include "cli.h" +#include "mops.h" + + + +int cmd_bpdu_id (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + struct mops_ext_bpdu * pd = clipkt->p_desc; + char str[256]; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc!=1) ) + { + mz_def16("0x0000", pd->id, str); + cli_print(cli, "Specify the BPDU identifier (0..65535)\r"); + cli_print(cli, "%s\n", str); + cli_print(cli, "\n"); + return CLI_OK; + } + + if (mops_pdesc_2byte(&pd->id, argv[0], 0, 0, 65535)) + { + cli_print(cli, "Specify a value between 0 and 65535\n"); + } + + return CLI_OK; +} + + +int cmd_bpdu_version (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + struct mops_ext_bpdu * pd = clipkt->p_desc; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc!=1) ) + { + cli_print(cli, "Specify the BPDU version (0..255)\r"); + cli_print(cli, "\n"); + return CLI_OK; + } + + if (mops_pdesc_1byte(&pd->version, argv[0], 0, 0, 255)) + { + cli_print(cli, "Specify a value between 0 and 255\n"); + } + + + return CLI_OK; +} + + + +int cmd_bpdu_type (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + struct mops_ext_bpdu * pd = clipkt->p_desc; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc!=1) ) { + cli_print(cli, "Specify the BPDU type, either via keyword or number (0..255)\n"); + cli_print(cli, "Keywords:\n"); + cli_print(cli, " conf .... Configuration BPDU\r"); + cli_print(cli, " tcn ..... Topology Change BPDU\r"); + cli_print(cli, "\n"); + return CLI_OK; + } + + if (mz_strcmp(argv[0], "configuration", 1)==0) { + pd->bpdu_type = 0x00; + } else if (mz_strcmp(argv[0], "tcn", 1)==0) { + pd->bpdu_type = 0x80; + } else if (mops_pdesc_1byte(&pd->bpdu_type, argv[0], 0, 0, 255)) { + cli_print(cli, "Specify a value between 0 and 255\n"); + } + + return CLI_OK; +} + + +int cmd_bpdu_flags (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + struct mops_ext_bpdu * pd = clipkt->p_desc; + int i; + char str[16]; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>8) ) + { + cli_print(cli, "Specify the BPDU flags by keyword.\r"); + cli_print(cli, "Note that not-mentioned flags will be set to zero!\n"); + cli_print(cli, "General keywords:\n"); + cli_print(cli, " ack .... Topology Change Acknowledgement\r"); + cli_print(cli, " tcn .... Topology Change Notification\r"); + cli_print(cli, "\r"); + cli_print(cli, "RSTP-specific keywords:\n"); + cli_print(cli, " agree .... Agreement\r"); + cli_print(cli, " prop .... Proposal\r"); + cli_print(cli, " fwd .... Forward State\r"); + cli_print(cli, " learn .... Learn State\r"); + cli_print(cli, "\r"); + cli_print(cli, " Port roles:\n"); + cli_print(cli, " unknown .... Unknown\r"); + cli_print(cli, " alt .... Alternate or Backup\r"); + cli_print(cli, " root .... Root\r"); + cli_print(cli, " desg .... Designated\r"); + cli_print(cli, "\n"); + return CLI_OK; + } + + // 7 6 5 4 3 2 1 0 + // tcnack agree fwd learn X X proposal TCN + // where XX is 00 unknown + // 01 alternate or backup + // 10 root + // 11 designated + + if (argc) + { + pd->flags = 0x00; // always reset to zero (= normal Configuration BPDU) + + for (i=0; i<argc; i++) + { + if (mz_strcmp(argv[i], "ack", 2)==0) pd->flags |= 0x80; + else + if (mz_strcmp(argv[i], "tcn", 2)==0) pd->flags |= 0x01; + else + if (mz_strcmp(argv[i], "agree", 2)==0) pd->flags |= 0x40; + else + if (mz_strcmp(argv[i], "fwd", 2)==0) pd->flags |= 0x20; + else + if (mz_strcmp(argv[i], "learn", 2)==0) pd->flags |= 0x10; + else + if (mz_strcmp(argv[i], "proposal", 2)==0) pd->flags |= 0x02; + else + if (mz_strcmp(argv[i], "unknown", 2)==0) pd->flags &= 0xf3; + else + if (mz_strcmp(argv[i], "alt", 2)==0) { pd->flags &= 0xf7; pd->flags |= 0x04; } + else + if (mz_strcmp(argv[i], "root", 2)==0) { pd->flags &= 0xfb; pd->flags |= 0x08; } + else + if (mz_strcmp(argv[i], "desg", 2)==0) pd->flags |= 0x0c; + } + // Feedback + char2bits(pd->flags, str); + cli_print(cli, "Flags: %s\n", str); + } + else + { + cli_print(cli, "No flags configured (use '?')\n"); + } + + return CLI_OK; +} + + + + + + + + + +int cmd_bpdu_rid (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + + struct mops_ext_bpdu * pd = clipkt->p_desc; + char p[64], e[64]; + int pri, esi, r; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>2) ) + { + cli_print(cli, "Specify the BPDU root identifier, using the following format:\n"); + cli_print(cli, " <priority>[:ext-sys-id] [interface | MAC-Address]\n"); + cli_print(cli, " <priority> ....... priority (0-15)\r"); + cli_print(cli, " <ext-sys-id> ....... extended system-id (0-4095)\n"); + cli_print(cli, "Optionally the MAC address of the root bridge can be given, either directly as arbitrary\r"); + cli_print(cli, "address (format: XX:XX:XX:XX:XX:XX) or by referring to an existing interface.\n"); + cli_print(cli, "Per default the MAC address of the default interface is used and a priority of zero.\n"); + cli_print(cli, "\n"); + return CLI_OK; + } + + if (argc==0) { + cli_print(cli, "Please specify at least the priority (use ?)\n"); + return CLI_OK; + } + + mz_tok(argv[0], ":", 2, p, e); + + pri = (int) str2int(p); + if (e!=NULL) + esi = (int) str2int(e); + else + esi = 0; + + if (argc==1) // no MAC given + { + r = mops_create_bpdu_bid (clipkt, pri, esi, NULL, 1); // 1 means RID (0 means BID) + } + else + r = mops_create_bpdu_bid (clipkt, pri, esi, argv[1], 1); // 1 means RID (0 means BID) + + + // Check return value + switch (r) + { + case 1: + cli_print(cli, "Priority must be within 0..15\n"); + return CLI_OK; + break; + case 2: + cli_print(cli, "Extended System-ID must be within 0..4095\n"); + return CLI_OK; + break; + case 3: + cli_print(cli, "Invalid MAC address or interface\n"); + return CLI_OK; + break; + case 4: + cli_print(cli, "Invalid format - use ?\n"); + return CLI_OK; + break; + } + + + + //--------- + // Verify: + bs2str(pd->root_id, p, 8); + cli_print(cli, "RID is now %s\n", p); + // ------- + // + return CLI_OK; +} + + + + + + +int cmd_bpdu_pc (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + struct mops_ext_bpdu * pd = clipkt->p_desc; + unsigned long long int i; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>1) ) + { + cli_print(cli, "Specify the BPDU root path cost (0..4294967295)\r"); + cli_print(cli, "\n"); + return CLI_OK; + } + + if (argc==0) + { + cli_print(cli, "Missing argument (use ?)\n"); + return CLI_OK; + } + + i = str2lint (argv[0]); + if (i>0xffffffff) + { + cli_print(cli, "Range exceeded (0..4294967295)\n"); + } + else + pd->root_pc = (u_int32_t) i; + + return CLI_OK; +} + + + + +int cmd_bpdu_bid (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + + struct mops_ext_bpdu * pd = clipkt->p_desc; + char p[64], e[64]; + int pri, esi, r; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>2) ) + { + cli_print(cli, "Specify the BPDU bridge identifier, using the following format:\n"); + cli_print(cli, " <priority>[:ext-sys-id] [interface | MAC-Address]\n"); + cli_print(cli, " <priority> ....... priority (0-15)\r"); + cli_print(cli, " <ext-sys-id> ....... extended system-id (0-4095)\n"); + cli_print(cli, "Optionally the MAC address of the root bridge can be given, either directly as arbitrary\r"); + cli_print(cli, "address (format: XX:XX:XX:XX:XX:XX) or by referring to an existing interface.\n"); + cli_print(cli, "Per default the MAC address of the default interface is used and a priority of zero.\n"); + cli_print(cli, "\n"); + return CLI_OK; + } + + + if (argc==0) + { + cli_print(cli, "Please specify at least the priority (use ?)\n"); + return CLI_OK; + } + + mz_tok(argv[0], ":", 2, p, e); + + pri = (int) str2int(p); + if (e!=NULL) + esi = (int) str2int(e); + else + esi = 0; + + if (argc==1) // no MAC given + { + r = mops_create_bpdu_bid (clipkt, pri, esi, NULL, 0); // 0 means BID (1 means RID) + } + else + r = mops_create_bpdu_bid (clipkt, pri, esi, argv[1], 0); // 0 means BID (1 means RID) + + + // Check return value + switch (r) + { + case 1: + cli_print(cli, "Priority must be within 0..15\n"); + return CLI_OK; + break; + case 2: + cli_print(cli, "Extended System-ID must be within 0..4095\n"); + return CLI_OK; + break; + case 3: + cli_print(cli, "Invalid MAC address or interface\n"); + return CLI_OK; + break; + case 4: + cli_print(cli, "Invalid format - use ?\n"); + return CLI_OK; + break; + } + + + + //--------- + // Verify: + bs2str(pd->bridge_id, p, 8); + cli_print(cli, "BID is now %s\n", p); + // ------- + // + return CLI_OK; +} + + + + + +int cmd_bpdu_pid (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + struct mops_ext_bpdu * pd = clipkt->p_desc; + u_int32_t i; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>1) ) + { + cli_print(cli, "Specify the BPDU port identifier (0..65535)\r"); + cli_print(cli, "\n"); + return CLI_OK; + } + + if (argc==0) + { + cli_print(cli, "Missing argument (use ?)\n"); + return CLI_OK; + } + + i = (u_int32_t) str2int (argv[0]); + + if (i>0xffff) + { + cli_print(cli, "The port identifier must be within 0..65535\n"); + return CLI_OK; + } + + pd->port_id = (u_int16_t) i; + + return CLI_OK; +} + +// +// +// NOTE: +// +// All timers are multiples of 1/256 sec. Thus times range from 0 to 255 seconds. +// +// + +int cmd_bpdu_age (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + struct mops_ext_bpdu * pd = clipkt->p_desc; + u_int32_t i; + char str[256]; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>2) ) + { + mz_def16("0", pd->message_age, str); + + cli_print(cli, "Specify the message age:\n"); + cli_print(cli, " - either in seconds (0..256) e. g. '14 sec'\r"); + cli_print(cli, " - or as multiples of 1/256 seconds (0..65535)\n"); + cli_print(cli, "%s\n", str); + cli_print(cli, "\n"); + return CLI_OK; + } + + if (argc==0) + { + cli_print(cli, "Missing argument (use ?)\n"); + return CLI_OK; + } + + i = (u_int32_t) str2int (argv[0]); + + if (argc==1) // absolute + { + if (i>0xffff) + cli_print(cli, "The age must be within 0..65535\n"); + else + pd->message_age = (u_int16_t) i; + } + else if (mz_strcmp(argv[1], "seconds", 1)==0) // in seconds + { + if (i>256) + cli_print(cli, "The age must be within 0..256 seconds\n"); + else + { + if (i==256) + i = 0xffff; // since 256*256=65536 which exceeds 0xffff but 65535/256 = 255.996 + else + i = i * 256; + + pd->message_age = (u_int16_t) i; + } + + } + else + cli_print(cli, "Invalid argument\n"); + + return CLI_OK; + +} + + + + + + + +int cmd_bpdu_maxage (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + struct mops_ext_bpdu * pd = clipkt->p_desc; + u_int32_t i; + char str[256]; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>2) ) + { + mz_def16("20 seconds", pd->max_age, str); + + cli_print(cli, "Specify the maximum message age:\n"); + cli_print(cli, " - either in seconds (0..256) e. g. '20 sec'\r"); + cli_print(cli, " - or as multiples of 1/256 seconds (0..65535)\n"); + cli_print(cli, "%s\n", str); + cli_print(cli, "\n"); + return CLI_OK; + } + + if (argc==0) + { + cli_print(cli, "Missing argument (use ?)\n"); + return CLI_OK; + } + + i = (u_int32_t) str2int (argv[0]); + + if (argc==1) // absolute + { + if (i>0xffff) + cli_print(cli, "The max age must be within 0..65535\n"); + else + pd->max_age = (u_int16_t) i; + } + else if (mz_strcmp(argv[1], "seconds", 1)==0) // in seconds + { + if (i>256) + cli_print(cli, "The max age must be within 0..256 seconds\n"); + else + { + if (i==256) + i = 0xffff; // since 256*256=65536 which exceeds 0xffff but 65535/256 = 255.996 + else + i = i * 256; + + pd->max_age = (u_int16_t) i; + } + + } + else + cli_print(cli, "Invalid argument\n"); + + return CLI_OK; + +} + + + + + +int cmd_bpdu_hello (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + struct mops_ext_bpdu * pd = clipkt->p_desc; + u_int32_t i; + char str[256]; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>2) ) + { + mz_def16("2 seconds", pd->hello_time, str); + + cli_print(cli, "Specify the hello interval:\n"); + cli_print(cli, " - either in seconds (0..256) e. g. '2 sec'\r"); + cli_print(cli, " - or as multiples of 1/256 seconds (0..65535)\n"); + cli_print(cli, "%s\n", str); cli_print(cli, "\n"); + return CLI_OK; + } + + if (argc==0) + { + cli_print(cli, "Missing argument (use ?)\n"); + return CLI_OK; + } + + + i = (u_int32_t) str2int (argv[0]); + + if (argc==1) // absolute + { + if (i>0xffff) + cli_print(cli, "The hello interval must be within 0..65535\n"); + else + pd->hello_time = (u_int16_t) i; + } + else if (mz_strcmp(argv[1], "seconds", 1)==0) // in seconds + { + if (i>256) + cli_print(cli, "The hello interval must be within 0..256 seconds\n"); + else + { + if (i==256) + i = 0xffff; // since 256*256=65536 which exceeds 0xffff but 65535/256 = 255.996 + else + i = i * 256; + + pd->hello_time = (u_int16_t) i; + } + + } + else + cli_print(cli, "Invalid argument\n"); + + return CLI_OK; + +} + +int cmd_bpdu_fwd (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + struct mops_ext_bpdu * pd = clipkt->p_desc; + u_int32_t i; + char str[256]; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>2) ) + { + mz_def16("15 seconds", pd->f_delay, str); + + cli_print(cli, "Specify the forward delay:\n"); + cli_print(cli, " - either in seconds (0..256) e. g. '15 sec'\r"); + cli_print(cli, " - or as multiples of 1/256 seconds (0..65535)\n"); + cli_print(cli, "%s\n", str); + cli_print(cli, "\n"); + return CLI_OK; + } + + if (argc==0) + { + cli_print(cli, "Missing argument (use ?)\n"); + return CLI_OK; + } + + + i = (u_int32_t) str2int (argv[0]); + + if (argc==1) // absolute + { + if (i>0xffff) + cli_print(cli, "The forward delay must be within 0..65535\n"); + else + pd->f_delay = (u_int16_t) i; + } + else if (mz_strcmp(argv[1], "seconds", 1)==0) // in seconds + { + if (i>256) + cli_print(cli, "The forward delay must be within 0..256 seconds\n"); + else + { + if (i==256) + i = 0xffff; // since 256*256=65536 which exceeds 0xffff but 65535/256 = 255.996 + else + i = i * 256; + + pd->f_delay = (u_int16_t) i; + } + + } + else + cli_print(cli, "Invalid argument\n"); + + return CLI_OK; + +} + + + +int cmd_bpdu_mode (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + struct mops_ext_bpdu * pd = clipkt->p_desc; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>1) ) + { + cli_print(cli, "Specify the BPDU mode using the keywords:\n"); + cli_print(cli, " stp ...... IEEE 802.1d (traditional CST)\r"); + cli_print(cli, " rstp ...... IEEE 802.1w (Rapid STP)\r"); + cli_print(cli, " mstp ...... IEEE 802.1s (Multiple STP)\r"); + cli_print(cli, " pvst ...... Cisco Per-VLAN STP\r"); + cli_print(cli, " rpvst ...... Cisco Per-VLAN RSTP\r"); + cli_print(cli, "\n"); + return CLI_OK; + } + + if (argc==0) + { + cli_print(cli, "Missing argument (use ?)\n"); + return CLI_OK; + } + + + if (mz_strcmp(argv[0], "stp", 1)==0) + { + pd->version=0; + pd->rstp=0; + pd->pvst=0; + pd->mstp=0; + } + else if (mz_strcmp(argv[0], "rstp", 2)==0) + { + pd->version=2; + pd->rstp=1; + pd->mstp=0; + } + else if (mz_strcmp(argv[0], "mstp", 1)==0) + { + pd->version=3; + pd->mstp=1; + } + else if (mz_strcmp(argv[0], "pvst", 1)==0) + { + pd->version=0; + pd->pvst=1; + pd->rstp=0; + pd->mstp=0; + } + else if (mz_strcmp(argv[0], "rpvst", 2)==0) + { + pd->version=2; + pd->rstp=1; + pd->pvst=1; + pd->mstp=0; + } + + + // TODO: also change version to 2 if RSTP, 0 if legacy + + + return CLI_OK; +} + + + + +int cmd_bpdu_vlan(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + u_int32_t i; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>1) ) + { + cli_print(cli, "Specify the VLAN number for PVST+ messages (0..4095)\n"); + cli_print(cli, "\n"); + return CLI_OK; + } + + if (argc==0) + { + cli_print(cli, "Missing argument (use ?)\n"); + return CLI_OK; + } + + i = (u_int32_t) str2int(argv[0]); + + if (i>65535) + { + cli_print(cli, "VLAN number is definitely too large! (0..65535 at maximum)\n"); + return CLI_OK; + } + + if (i>4095) + { + cli_print(cli, "Warning: Invalid VLAN number (0..4095) - but let's try it...\n"); + } + + mops_create_bpdu_trailer(clipkt, (u_int16_t) i); + + return CLI_OK; +} + + + +int cmd_bpdu_end(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + char prompt[16]; + sprintf(prompt, "pkt-%i",clipkt->id); + cli_set_configmode(cli, MZ_MODE_PACKET, prompt); + return CLI_OK; +} + diff --git a/staging/cli_cmds.c b/staging/cli_cmds.c new file mode 100644 index 0000000..3304410 --- /dev/null +++ b/staging/cli_cmds.c @@ -0,0 +1,1428 @@ +/* + * 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 + * +*/ + + + +#include "mz.h" +#include "cli.h" +#include "mops.h" +#include "llist.h" + +// Callback functions for the commands. +// __FUNCTION__ contains the name of the current callback function (for troubleshootig) + + +//////////////////////////////////////////////////////////////////////////////// +int cmd_test(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + cli_print(cli, "called %s with %s\r\n", __FUNCTION__, command); + return CLI_OK; +} + + + +//////////////////////////////////////////////////////////////////////////////// +int debug_all (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + if ( strncmp(argv[argc-1], "?", 1) == 0) + { + cli_print(cli, "Will debug everything. (Be careful!)\n"); + return CLI_OK; + } + + + cli_debug = 0x7fff; + cli_print(cli, "Debug all enabled - stop with undebug all\r"); + cli_print(cli, "Note: _Already_ active packets will not be omitted!\n"); + + if (mz_strcmp(argv[argc-1], "dev", 3)==0) + { + cli_print(cli, "*** Developer mode debugging enabled ***\n"); + cli_debug = 0xffff; + } + + return CLI_OK; +} + + + + +//////////////////////////////////////////////////////////////////////////////// +// Clear all _legacy_ Mausezahn settings (reinitialize anything) +int clear_all(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + if (argc) { + cli_print(cli, "No argument required! Try again.\n"); + return CLI_OK; + } + + reset(); + cli_print(cli, "All legacy Mausezahn parts have been reinitialized.\r"); + mops_delete_all(mp_head); + mops_reset_packet (mp_head); + cli_print(cli, "MOPS has been reinitialized.\n"); + return CLI_OK; +} + + +int clear_packet(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + + struct mops *cur; + u_int32_t i; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc!=1) ) + { + cli_print(cli, "Delete a single packet (i. e. MOPS entry).\r"); + cli_print(cli, "Expects a single argument which is either a packet's ID or name.\r"); + cli_print(cli, "NOTE: If the name matches an ID then the name has higher preference.\n"); + return CLI_OK; + } + + + if (argc!=1) { + cli_print(cli, "Please specify only the packets ID or name\n"); + return CLI_OK; + } + + cur = mops_search_name (mp_head, argv[0]); + if (cur==NULL) { + i = (u_int32_t) str2int (argv[0]); + cur = mops_search_id (mp_head, i); + if (cur==NULL) { + cli_print(cli, "No packet found with that ID or name!\n"); + return CLI_OK; + } + } + clipkt = mops_delete_packet(cur); + cli_print(cli, "Packet deleted.\n"); + return CLI_OK; +} + + +int cmd_reset_packet(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + struct mops *cur; + u_int32_t i; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc!=1) ) + { + cli_print(cli, "Resets a single packet (i. e. MOPS entry).\r"); + cli_print(cli, "Expects a single argument which is either a packet's ID or name.\r"); + cli_print(cli, "NOTE: If the name matches an ID then the name has higher preference.\n"); + return CLI_OK; + } + + + if (argc!=1) { + cli_print(cli, "Please specify only the packets ID or name\n"); + return CLI_OK; + } + + cur = mops_search_name (mp_head, argv[0]); + if (cur==NULL) { + i = (u_int32_t) str2int (argv[0]); + cur = mops_search_id (mp_head, i); + if (cur==NULL) { + cli_print(cli, "No packet found with that ID or name!\n"); + return CLI_OK; + } + } + + mops_reset_packet(cur); + cli_print(cli, "New packet name: %s\n", cur->packet_name); + return CLI_OK; +} + + + + +int show_system(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + cli_print(cli, "Not supported in this version\n"); + return CLI_OK; +} + + + + +//////////////////////////////////////////////////////////////////////////////// +// Run through packet list and print some details about existing packets. +// SYNTAX: +// +// show packet +// show packet MyPacket +// +int show_packets(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + int a=0, i, j=0, k, v, active_only=0; + u_int32_t t; + char c,T; + char name[32], ds[16], pr[16], ps[16]; + char myframe[MAX_MOPS_FRAME_SIZE*3]; + char mystate[32]; + char line[150], line2[150], line3[150]; + char delay_str[64]; + unsigned char *x0, *x1, *x2, *x3; + + struct mops *head = mp_head; + struct mops *mp = mp_head; + + + if (strncmp(argv[argc-1], "?", 2)==0) { + cli_print(cli, "<CR> Show list of all defined packets\r"); + cli_print(cli, "active Only show active packets\r"); + cli_print(cli, "<PKT_ID> Show detailed info about given packet\r"); +//TODO cli_print(cli, "type <proto> Only list packets r"); + cli_print(cli, "\n"); + return CLI_OK; + } + + if (argc==1) { + if (mz_strcmp(argv[0], "active", 1)==0) { + active_only=1; + } + } + + if ((argc==0) || (active_only)) // show packet summary + { + cli_print(cli, "Packet layer flags: E=Ethernet, S=SNAP, Q=802.1Q, M=MPLS, I/i=IP/delivery_off, U=UDP, T=TCP\n"); + cli_print(cli, "PktID PktName Layers Proto Size State Device Delay Count/CntX\n"); + + do + { + if (active_only) { + if (mp->state < MOPS_STATE_ACTIVE) { + mp = mp->next; + j++; + continue; + } + } + + ds[0]='\0'; + ps[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; + } + + + switch (mops_state(mp)) + { + case MOPS_STATE_NULL: + strcat(ps, "NULL"); // should never happen! + break; + case MOPS_STATE_INIT: + strcat(ps, "init"); + break; + case MOPS_STATE_CONFIG: + strcat(ps, "config"); + break; + case MOPS_STATE_ACTIVE: + strcat(ps, "active"); + a++; + break; + case MOPS_STATE_SEQACT: + strcat(ps, "actseq"); + a++; + break; + default: + strcat(ps, "unknown"); + break; + } + + switch (mp->interval_used) { + case 1: // interval only configured, not started + strncat(ps, "-i", 2); + break; + + case 2: + strncat(ps, "+I", 2); + break; + default: + break; + } + + + strncpy (name, mp->packet_name, 13); // only show first 13 chars + + if (strnlen(mp->packet_name, MAX_MOPS_PACKET_NAME_LEN)>13) + { + name[13]=0x00; + strcat(name, "..."); + } + + // To determine the actual packet length *** + // we must reassemble everything: *** + mops_ext_update (mp); + mops_update (mp); + + timespec2str(&mp->ndelay, delay_str); + + // ID name lrs prot size state dev del count/cntx/% + sprintf(line, "%5i %-16s %s %-8s %4i %-9s %-6s %10s%9lu/%lu (%i%%)\r", + mp->id, // ID + name, // packet_name + ds, // layers + pr, // protocol + mp->frame_s, // size + ps, // state + mp->device, // device + delay_str, // delay + mp->count, // Configured count value + mp->cntx, // Current count + (mp->count) ? (int) (100 * (mp->count - mp->cntx)/mp->count) : 0 ); + cli_print(cli, "%s\r", line); + mp = mp->next; + j++; + } + while (head != mp); + + cli_print(cli, "\r"); + cli_print(cli, "%i packets defined, %i active.\n", j, a); + } + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + else if (argc == 1) // show details about a specific packet ********************************************** + { + if ( (mp = mops_search_name (mp_head, argv[0])) == NULL)// not found + { + if ( (mp = mops_search_id (mp_head, (int) str2int(argv[0]))) == NULL)// not found + { + cli_print (cli, "Packet not in list.\n"); + return CLI_OK; + } + } + + // To determine the actual packet length *** + // we must reassemble everything: *** + mops_ext_update (mp); + mops_update (mp); + + cli_print(cli, "Packet [%i] %s\r", mp->id, mp->packet_name); + cli_print(cli, " Description: %s \r", + (strnlen(mp->description, MAX_MOPS_DESCRIPTION_LEN)) ? mp->description : "(no description)"); + + switch(mp->state) + { + case MOPS_STATE_NULL: + sprintf(mystate, "NULL"); + break; + case MOPS_STATE_INIT: + sprintf(mystate, "init"); + break; + case MOPS_STATE_CONFIG: + sprintf(mystate, "config"); + break; + case MOPS_STATE_ACTIVE: + sprintf(mystate, "active(tx)"); + break; + default: + sprintf(mystate, "unknown"); + } + + timespec2str(&mp->ndelay, delay_str); + if (mp->interval_used) + timespec2str(&mp->interval, line2); + else + sprintf(line2, "(undefined)"); + + sprintf(line, "State: %s, Count=%lu, delay=%s (%lu s %lu nsec), interval= %s\r", + mystate, + mp->count, + delay_str, + mp->ndelay.tv_sec, + mp->ndelay.tv_nsec, + line2); + cli_print(cli, " %s\r", line); + + cli_print(cli, " Headers:\r"); + i=0; + if (mp->use_ETHER) + { + if (mp->eth_src_israndom) + { + cli_print(cli, " Ethernet: *** RANDOMIZED SMAC *** => %02x-%02x-%02x-%02x-%02x-%02x [%04x%s]\r", + mp->eth_dst[0],mp->eth_dst[1],mp->eth_dst[2],mp->eth_dst[3],mp->eth_dst[4],mp->eth_dst[5], + mp->eth_type, (mp->use_dot1Q) ? " after 802.1Q tag" : ""); + } + else + { + cli_print(cli, " Ethernet: %02x-%02x-%02x-%02x-%02x-%02x => %02x-%02x-%02x-%02x-%02x-%02x [%04x%s]\r", + mp->eth_src[0],mp->eth_src[1],mp->eth_src[2],mp->eth_src[3],mp->eth_src[4],mp->eth_src[5], + mp->eth_dst[0],mp->eth_dst[1],mp->eth_dst[2],mp->eth_dst[3],mp->eth_dst[4],mp->eth_dst[5], + mp->eth_type, (mp->use_dot1Q) ? " after 802.1Q tag" : ""); + } + + if (mp->use_IP) { + if (mp->auto_delivery_off) + cli_print(cli, " NOTE: Auto-delivery is OFF (that is, the destination MAC is fixed)\r"); + else + cli_print(cli, " Auto-delivery is ON (that is, the actual MAC is determined upon transmission)\r"); + } + i++; + } + if (mp->use_SNAP) + { + bs2str(clipkt->eth_snap, line, clipkt->eth_snap_s); + cli_print(cli, " LLC/SNAP: %s\r", line); + i++; + } + if (mp->use_dot1Q) + { + k = clipkt->dot1Q_s/4; // number of tags + sprintf(line, "%i tag(s); ", k); + for (j=0; j<k; j++) + { // tag format = 0x81 0x00 cosTvvvv vvvvvvvv + // x0 x1 + x0 = (unsigned char*) &clipkt->dot1Q[(j*4)+2]; + x1 = (unsigned char*) &clipkt->dot1Q[(j*4)+3]; + v = (*x0 & 0x0f)*256 + *x1; // VLAN +// c = *x0 & 0xe0; // CoS e0=11100000 + c = *x0 >> 5; + sprintf(ds, "%i:%i%s", + v, + (unsigned char) c, + (*x0 & 0x10) ? "[CFI]" : ""); // CFI + strncat(line, ds, 14); + if (j<(k-1)) strcat(line, ", "); + } + + cli_print(cli, " 802.1Q: %s (VLAN:CoS)\r", line); + i++; + } + if (mp->use_MPLS) + { + k = clipkt->mpls_s/4; // number of tags + sprintf(line, "%i tag(s); ", k); + for (j=0; j<k; j++) + { // tag format = llllllll llllllll llllcccB TTTTTTTT + x0 = (unsigned char*) &clipkt->mpls[(j*4)+0]; + x1 = (unsigned char*) &clipkt->mpls[(j*4)+1]; + x2 = (unsigned char*) &clipkt->mpls[(j*4)+2]; + x3 = (unsigned char*) &clipkt->mpls[(j*4)+3]; + t = *x0; + t <<= 12; + t += *x1 * 16; + t += (*x2 & 0xf0) >> 4; + c = (*x2 & 0x0e) >> 1; + T = *x3; + sprintf(ds, "%i:%i:%i%s", + t, + (unsigned char) c, + (unsigned char) T, + (*x2 & 0x01) ? "[BoS]" : ""); // Bottom of Stack? + strncat(line, ds, 20); + if (j<(k-1)) strcat(line, ", "); + } + + cli_print(cli, " MPLS: %s (Label:CoS:TTL)\r", line); + + i++; + } + if (mp->use_IP) + { + // Source IP settings: + x0 = (unsigned char*) & clipkt->ip_src; + line2[0]=0x00; + if (clipkt->ip_src_isrange) + { + x1 = (unsigned char*) & clipkt->ip_src_start; + x2 = (unsigned char*) & clipkt->ip_src_stop; + sprintf(line2, "%u.%u.%u.%u-%u.%u.%u.%u", + (unsigned char) *(x1+3), (unsigned char) *(x1+2), (unsigned char) *(x1+1) , (unsigned char) *x1, + (unsigned char) *(x2+3), (unsigned char) *(x2+2), (unsigned char) *(x2+1) , (unsigned char) *x2); + } + sprintf(line, "SA=%u.%u.%u.%u %s %s %s", + (unsigned char) *(x0+3), (unsigned char) *(x0+2), (unsigned char) *(x0+1) , (unsigned char) *x0, + (clipkt->ip_src_israndom) ? "RANDOM" : "(not random)", + (clipkt->ip_src_isrange) ? "RANGE:" : "(no range)", + line2); + + cli_print(cli, " IP: %s\r", line); + //Destination IP settings: + x0 = (unsigned char*) & clipkt->ip_dst; + line2[0]=0x00; + if (clipkt->ip_dst_isrange) + { + x1 = (unsigned char*) & clipkt->ip_dst_start; + x2 = (unsigned char*) & clipkt->ip_dst_stop; + sprintf(line2, "%u.%u.%u.%u-%u.%u.%u.%u", + (unsigned char) *(x1+3), (unsigned char) *(x1+2), (unsigned char) *(x1+1) , (unsigned char) *x1, + (unsigned char) *(x2+3), (unsigned char) *(x2+2), (unsigned char) *(x2+1) , (unsigned char) *x2); + } + + sprintf(line, "DA=%u.%u.%u.%u %s %s", + (unsigned char) *(x0+3), (unsigned char) *(x0+2), (unsigned char) *(x0+1) , (unsigned char) *x0, + (clipkt->ip_dst_isrange) ? "RANGE:" : "(no range)", + line2); + cli_print(cli, " %s\r", line); + + sprintf(line, "ToS=0x%02x proto=%u TTL=%u ID=%u offset=%u flags: %s|%s|%s", + clipkt->ip_tos, clipkt->ip_proto, clipkt->ip_ttl, clipkt->ip_id, clipkt->ip_frag_offset, + (clipkt->ip_flags_RS) ? "RS" : "-", + (clipkt->ip_flags_DF) ? "DF" : "-", + (clipkt->ip_flags_MF) ? "MF" : "-"); + + cli_print(cli, " %s\r", line); + + if (clipkt->ip_fragsize) { + sprintf(line, "NOTE: Auto-fragmentation is ON! Fragment size %u bytes, overlap %u", + clipkt->ip_fragsize, + clipkt->ip_frag_overlap); + cli_print(cli, " %s\r", line); + } + + sprintf(line, "len=%u(%s) checksum=0x%02x%02x(%s)", + clipkt->frame[clipkt->begin_IP+2]*256+clipkt->frame[clipkt->begin_IP+3], + (clipkt->ip_len_false) ? "false" : "correct", + clipkt->frame[clipkt->begin_IP+10], + clipkt->frame[clipkt->begin_IP+11], + (clipkt->ip_sum_false) ? "false" : "correct"); + + cli_print(cli, " %s\r", line); + + i++; + } + if (mp->use_UDP) + { + if (clipkt->sp_isrange) + sprintf(line2, "RANGE: %u-%u", clipkt->sp_start, clipkt->sp_stop); + else + sprintf(line2, "(norange)"); + if (clipkt->dp_isrange) + sprintf(line3, "RANGE: %u-%u", clipkt->dp_start, clipkt->dp_stop); + else + sprintf(line3, "(norange)"); + sprintf(line, "SP=%i %s %s, DP=%i %s %s\r", + clipkt->sp, + line2, + (clipkt->sp_isrand) ? "RANDOM" : "(not random)", + clipkt->dp, + line3, + (clipkt->dp_isrand) ? "RANDOM" : "(not random)"); + cli_print(cli, " UDP: %s\r", line); + sprintf(line, "checksum= %04x (%s), length= %u (%s)", + clipkt->udp_sum, (clipkt->udp_sum_false) ? "false" : "correct", + clipkt->udp_len, (clipkt->udp_len_false) ? "false" : "correct"); + cli_print(cli, " %s\r", line); + i++; + } + if (mp->use_TCP) + { + sprintf(line, "%u bytes segment size (including TCP header)", mp->tcp_len); + cli_print(cli, " TCP: %s\r", line); + if (clipkt->sp_isrange) + sprintf(line2, "RANGE: %u-%u", clipkt->sp_start, clipkt->sp_stop); + else + sprintf(line2, "(norange)"); + if (clipkt->dp_isrange) + sprintf(line3, "RANGE: %u-%u", clipkt->dp_start, clipkt->dp_stop); + else + sprintf(line3, "(norange)"); + sprintf(line, "SP=%i %s %s, DP=%i %s %s\r", + clipkt->sp, + line2, + (clipkt->sp_isrand) ? "RANDOM" : "(not random)", + clipkt->dp, + line3, + (clipkt->dp_isrand) ? "RANDOM" : "(not random)"); + cli_print(cli, " %s\r", line); + sprintf(line, "SQNR=%u (start %u, stop %u, delta %u) -- ACKNR=%u %s", + clipkt->tcp_seq, + clipkt->tcp_seq_start, + clipkt->tcp_seq_stop, + clipkt->tcp_seq_delta, + clipkt->tcp_ack, + (clipkt->tcp_ctrl_ACK) ? "(valid)" : "(invalid)"); + cli_print(cli, " %s\r", line); + mops_tcp_flags2str(clipkt,line2); + sprintf(line, "Flags: %s, reserved field is %02x, urgent pointer= %u", + line2, + clipkt->tcp_res, + clipkt->tcp_urg); + cli_print(cli, " %s\r", line); + sprintf(line, "Announced window size= %u", clipkt->tcp_win); + cli_print(cli, " %s\r", line); + sprintf(line, "Offset= %u (times 32 bit; value is %s), checksum= %04x (%s)", + clipkt->tcp_offset, + (clipkt->tcp_offset_false) ? "FALSE" : "valid", + clipkt->tcp_sum, + (clipkt->tcp_sum_false) ? "FALSE" : "valid"); + cli_print(cli, " %s\r", line); + sprintf(line, "%s - %u bytes defined", + (clipkt->tcp_option_used) ? "TCP options attached" : "(No TCP options attached)", + clipkt->tcp_option_s); + cli_print(cli, " %s\r", line); + i++; + } + + if (!i) cli_print(cli, " No headers defined.\r"); + + if (mp->msg_s) { + cli_print(cli, " Payload size: %i bytes\r", mp->msg_s); + } + + cli_print(cli, " Frame size: %i bytes\n", mp->frame_s); + + mops_print_frame(mp, myframe); + cli_print(cli, "%s\n", myframe); + } + + return CLI_OK; +} + + +//////////////////////////////////////////////////////////////////////////////// +int show_interfaces (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + int i, j=0; + char line[100]; + char ip[20]; + + + if (strncmp(argv[argc-1], "?", 2)==0) { + cli_print(cli, "<CR> Show summary list of all interfaces found\r"); + cli_print(cli, "detailed Additionally show network, mask, default gatway, and MTU\r"); + cli_print(cli, "\n"); + return CLI_OK; + } + + // Some safety checks + if (argc>1) return CLI_OK; + if (argc==1) { + if (mz_strcmp(argv[0], "detailed", 1)!=0) { + cli_print(cli, "invalid keyword (use ?)\n"); + return CLI_OK; + } + } + + /* Refresh interface data */ + + lookupdev(); + + for (i=0; i<device_list_entries; i++) { + get_dev_params(device_list[i].dev); + } + + + + /* No additional keyword */ + if (argc==0) { + cli_print(cli, "Available network interfaces:\n"); + cli_print(cli, " real real used (fake) used (fake)\r"); + cli_print(cli, " device IPv4 address MAC address IPv4 address MAC address\r"); + cli_print(cli, "---------------------------------------------------------------------------------------\r"); + for (i=0; i<device_list_entries; i++) { + sprintf(ip,"%u.%u.%u.%u", + device_list[i].ip_mops[0], + device_list[i].ip_mops[1], + device_list[i].ip_mops[2], + device_list[i].ip_mops[3]); + + sprintf(line, "%-10s %-15s %02x:%02x:%02x:%02x:%02x:%02x %-15s %02x:%02x:%02x:%02x:%02x:%02x", + device_list[i].dev, device_list[i].ip_str, + device_list[i].mac[0], + device_list[i].mac[1], + device_list[i].mac[2], + device_list[i].mac[3], + device_list[i].mac[4], + device_list[i].mac[5], + ip, + device_list[i].mac_mops[0], + device_list[i].mac_mops[1], + device_list[i].mac_mops[2], + device_list[i].mac_mops[3], + device_list[i].mac_mops[4], + device_list[i].mac_mops[5] + ); + + + if (strncmp(device_list[i].dev, tx.device, 16)==0) { + cli_print(cli, "%s%s> %s\r", + (device_list[i].cli) ? "C" : " ", + (device_list[i].mgmt_only) ? "!" : "", + line); + j=i; + } + else + cli_print(cli, "%s%s %s\r", + (device_list[i].cli) ? "C" : " ", + (device_list[i].mgmt_only) ? "M" : "", + line); + } + } + ///////////////////////// + else + + /* keyword detailed used */ + if (mz_strcmp(argv[0], "detailed", 1)==0) { + cli_print(cli, "Detailed interface list:\n"); + for (i=0; i<device_list_entries; i++) { + sprintf(line, "interface %s [%i] %s%stype %s, MTU=%i bytes", // general HW info + device_list[i].dev, + device_list[i].index, + (device_list[i].cli) ? "[cli] " : "", + (device_list[i].mgmt_only) ? "[management-only] " : "", + (device_list[i].phy) ? "physical" : "software", + device_list[i].mtu); + cli_print(cli,"%s\r",line); + sprintf(line, "MAC bia: %02x:%02x:%02x:%02x:%02x:%02x\n MAC fake: %02x:%02x:%02x:%02x:%02x:%02x", + device_list[i].mac[0], + device_list[i].mac[1], + device_list[i].mac[2], + device_list[i].mac[3], + device_list[i].mac[4], + device_list[i].mac[5], + device_list[i].mac_mops[0], + device_list[i].mac_mops[1], + device_list[i].mac_mops[2], + device_list[i].mac_mops[3], + device_list[i].mac_mops[4], + device_list[i].mac_mops[5]); + cli_print(cli," %s\r",line); + sprintf(line,"IP addr: %s mask %u.%u.%u.%u (net %u.%u.%u.%u)", + device_list[i].ip_str, + device_list[i].mask[0], + device_list[i].mask[1], + device_list[i].mask[2], + device_list[i].mask[3], + device_list[i].net[0], + device_list[i].net[1], + device_list[i].net[2], + device_list[i].net[3]); + cli_print(cli," %s\r",line); + sprintf(line,"IP fake: %u.%u.%u.%u", + device_list[i].ip_mops[0], + device_list[i].ip_mops[1], + device_list[i].ip_mops[2], + device_list[i].ip_mops[3]); + cli_print(cli, " %s\r", line); + sprintf(line,"GW addr: %u.%u.%u.%u (%02x:%02x:%02x:%02x:%02x:%02x)", + device_list[i].ip_gw[0], + device_list[i].ip_gw[1], + device_list[i].ip_gw[2], + device_list[i].ip_gw[3], + device_list[i].mac_gw[0], + device_list[i].mac_gw[1], + device_list[i].mac_gw[2], + device_list[i].mac_gw[3], + device_list[i].mac_gw[4], + device_list[i].mac_gw[5]); + cli_print(cli," %s\n",line); + } + } + + /* In any case, print final summary line: */ + cli_print(cli, "\n%i interfaces found.\nDefault interface is %s.\n", + device_list_entries, device_list[j].dev); + + return CLI_OK; +} + + + +//////////////////////////////////////////////////////////////////////////////// +int show_set(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + unsigned char *x; + char hexload[3*MAX_PAYLOAD_SIZE]; + + cli_print(cli, "----- Packet parameters: ------ -------- Value: ----------\r"); + cli_print(cli, "Source MAC address (sa) %02x:%02x:%02x:%02x:%02x:%02x [%s]\r", + tx.eth_src[0], tx.eth_src[1], tx.eth_src[2], + tx.eth_src[3], tx.eth_src[4], tx.eth_src[5], + (tx.eth_src_rand) ? "rand" : "spec"); + cli_print(cli, "Basic MAC address %02x:%02x:%02x:%02x:%02x:%02x\r", + tx.eth_mac_own[0], tx.eth_mac_own[1], tx.eth_mac_own[2], + tx.eth_mac_own[3], tx.eth_mac_own[4], tx.eth_mac_own[5]); + cli_print(cli, "Destination MAC address (da) %02x:%02x:%02x:%02x:%02x:%02x [%s]\r", + tx.eth_dst[0], tx.eth_dst[1], tx.eth_dst[2], + tx.eth_dst[3], tx.eth_dst[4], tx.eth_dst[5], + (tx.eth_dst_rand) ? "rand" : "spec"); + cli_print(cli, "\r"); + x = (unsigned char *) &tx.ip_src; + cli_print(cli, "Source IP address (SA) %i.%i.%i.%i [%s]\r", + *x,*(x+1),*(x+2),*(x+3), + (tx.ip_src_rand) ? "rand" : "spec"); + + if (tx.ip_src_isrange) + { + x = (unsigned char *) &tx.ip_src_start; + cli_print(cli, "Source IP range start: %i.%i.%i.%i\r", + *(x+3), *(x+2), *(x+1), *x); + x = (unsigned char *) &tx.ip_src_stop; + cli_print(cli, "Source IP range stop: %i.%i.%i.%i\r", + *(x+3), *(x+2), *(x+1), *x); + } + else + { + cli_print(cli, "No source IP range specified\r"); + } + x = (unsigned char *) &tx.ip_dst; + cli_print(cli, "Destination IP address (DA) %i.%i.%i.%i\r", + *x,*(x+1),*(x+2),*(x+3)); + + if (tx.ip_dst_isrange) + { + x = (unsigned char *) &tx.ip_dst_start; + cli_print(cli, "Destination IP range start: %i.%i.%i.%i\r", + *(x+3), *(x+2), *(x+1), *x); + x = (unsigned char *) &tx.ip_dst_stop; + cli_print(cli, "Destination IP range stop: %i.%i.%i.%i\r", + *(x+3), *(x+2), *(x+1), *x); + } + else + { + cli_print(cli, "No destination IP range specified\r"); + } + + if (tx.dot1Q) + { + cli_print(cli, "802.1Q tags specified: %s\r", tx.dot1Q_txt); + } + + if (tx.mpls) + { + cli_print(cli, "MPLS labels specified: %s\r", tx.mpls_txt); + } + + if (tx.ascii) + { cli_print(cli, "\r"); + cli_print(cli, "---- ASCII payload is set: ----- \r"); + cli_print(cli, ">>>%s<<<\r", tx.ascii_payload); + cli_print(cli, "-------------------------------- \n"); + } + + if (tx.hex_payload_s) + { cli_print(cli, "\r"); + cli_print(cli, "---- Hexadecimal payload is set: ----- \r"); + bs2str(tx.hex_payload, hexload, tx.hex_payload_s); + cli_print(cli, "%s\r", hexload); + cli_print(cli, "-------------------------------------- \n"); + } + + if (tx.padding) + { + cli_print(cli, "Configured padding: %u\r", tx.padding); + } + + cli_print(cli, "\r"); + cli_print(cli, "Packet count value %u\r", tx.count); + cli_print(cli, "Interpacket delay (usec) %u\r", tx.delay); + cli_print(cli, "\r"); + cli_print(cli, "Used network device(s): %s\r", tx.device); + cli_print(cli, "\n"); + return CLI_OK; +} + + + + + + + +//////////////////////////////////////////////////////////////////////////////// +int stop_mausezahn (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + if (strncmp(argv[argc-1], "?", 2)==0) { + cli_print(cli, "now Terminate the mausezahn server! BEWARE!\n"); + return CLI_OK; + } + + if (argc!=1) { + cli_print(cli, "The only allowed argument is 'now' -- anything else is ignored\n"); + return CLI_OK; + } + + if (mz_strcmp(argv[0], "now", 3)==0) { + cli_print(cli, "Good bye...\n"); + cli_done(cli); + clean_up(0); + return CLI_OK; + } else { + cli_print(cli, "Invalid argument. If you want to stop the Mausezahn server then\r"); + cli_print(cli, "enter 'terminate now'. You cannot abbreviate the argument 'now'. \n"); + } + + return CLI_OK; +} + + + + +int cmd_run_id (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + int i, slot; + struct mops *mp; + + if (argc == 0) { + cli_print(cli, "Specify one or more packet identifiers to run.\n"); + return CLI_OK; + } + + if ( strncmp(argv[argc-1], "?", 1) == 0) { + cli_print(cli, "Run packet transmission processes for given list of packet identifiers\n"); + return CLI_OK; + } + + // User provided packet id numbers + if (argc > 0) { + for (i=0; i<argc; i++) { + slot = (int) str2int(argv[i]); + if ( (mp = mops_search_id (mp_head, slot)) == NULL) { // not found + cli_print (cli, "Packet %i not in list.\n", slot ); + return CLI_OK; + } + else { + switch (mops_tx_simple (mp)) { + case 1: + cli_print(cli, "Cannot create sending process.\r"); + return CLI_OK; + break; + case 3: + cli_print(cli, "Packet [%i] has already an active sending process\r", mp->id); + return CLI_OK; + break; + default: + cli_print (cli, "Activate [%i] ", slot ); + break; + } + } + } + cli_print (cli, "\n"); + } + return CLI_OK; +} + + +int cmd_run_name (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + int i; + struct mops *mp; + + if (argc == 0) { + cli_print(cli, "Specify one or more packet name(s) to run.\n"); + return CLI_OK; + } + + if ( strncmp(argv[argc-1], "?", 1) == 0) { + cli_print(cli, "Run packet transmission processes for specified packet name(s).\n"); + return CLI_OK; + } + + if (argc > 0) { + for (i=0; i<argc; i++) { + if ( (mp = mops_search_name (mp_head, argv[i])) == NULL) { // not found + cli_print (cli, "Packet %s not in list.\n", argv[i]); + return CLI_OK; + } + else { + switch (mops_tx_simple (mp)) { + case 1: + cli_print(cli, "Cannot create sending process.\r"); + return CLI_OK; + break; + case 3: + cli_print(cli, "Packet [%i] has already an active sending process\r", mp->id); + return CLI_OK; + break; + default: + cli_print (cli, "Activate [%i] ", mp->id ); + break; + } + } + } + cli_print (cli, "\n"); + } + return CLI_OK; +} + + +int cmd_run_sequence (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + struct mz_ll *cur; + int ret=0; + if (argc != 1) { + cli_print(cli, "Specify one (and only one) packet sequence name to run.\n"); + return CLI_OK; + } + + if ( strncmp(argv[argc-1], "?", 1) == 0) { + cli_print(cli, "Run sequence transmission processes for specified sequence name.\n"); + return CLI_OK; + } + + cur = mz_ll_search_name (packet_sequences, argv[0]); + if (cur==NULL) { // NOT FOUND !!! + cli_print(cli, "Sequence %s does not exist.", argv[0]); + return CLI_OK; + } + ret = mops_tx_sequence(cur); + switch (ret) { + case 0: cli_print(cli, "Sequence %s is runnning\n", cur->name); + break; + case 1: cli_print(cli, "Cannot run sequence: All packets must be in config state!\n"); + break; + case 2: cli_print(cli, "Cannot run sequence: All packets must have a finite count!\n"); + break; + case 3: cli_print(cli, "Cannot run sequence: Unable to start sequence transmission process.\n"); + break; + } + return CLI_OK; +} + + + +int cmd_run_all (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + int i; + struct mops *mp; + struct mops *head; + + if ( strncmp(argv[argc-1], "?", 1) == 0) { + cli_print(cli, "Run all user-specified packets.\n"); + return CLI_OK; + } + + if (argc>0) { + cli_print(cli, "No arguments expected!\n"); + return CLI_OK; + } + + // Send all valid packets + i=0; + head = mp_head; + mp = mp_head; + do { + if ((mp->mz_system==0) && (mops_state(mp) == MOPS_STATE_CONFIG)) { + switch (mops_tx_simple (mp)) { + case 1: + cli_print(cli, "Cannot create sending process.\r"); + return CLI_OK; + break; + case 3: + cli_print(cli, "Packet [%i] has already an active sending process\r", mp->id); + return CLI_OK; + break; + default: + break; + } + i++; + cli_print (cli, "Activate [%i] %s\r", mp->id, mp->packet_name ); + } + mp = mp->next; + } + while (head != mp); + if (i==0) { + cli_print (cli, "No valid packets found\n"); + } else { + cli_print (cli, "\r"); + cli_print (cli, "Activated %i packets \n", i); + } + return CLI_OK; +} + + + +int cmd_stop (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + struct mops *mp; + int i, ret=0, slot=0; + + struct mops *head = mp_head; + struct mops *cur = mp_head; + + if ((strncmp(argv[argc-1], "?", 2)==0) || (argc==0)) { + cli_print(cli, "Stop transmission process(es) or an active sequence.\r"); + cli_print(cli, "SYNTAX: 1) Either specify one or more packet-ids or packet names of active packets\r"); + cli_print(cli, " 2) Or enter 'sequence <seq-name>' to stop an active sequence and its associated packets.\n"); + return CLI_OK; + } + + // Did the user specify a sequence? (ONE SEQUENCE ONLY) + if ((mz_strcmp(argv[0], "sequence", 3)==0) && (argc==2)) { + ret = stop_sequence (argv[1]); + switch (ret) { + case 0: + cli_print(cli, "Sequence '%s' stopped.\n", argv[1]); + break; + + case 1: + cli_print(cli, "Sequence '%s' does not exist!\n", argv[1]); + break; + case 2: + cli_print(cli, "Sequence '%s' is not active. Nothing to stop.\n", argv[1]); + break; + } + return CLI_OK; + } + + + if (((mz_strcmp(argv[0], "all", 3)==0) || (mz_strcmp(argv[0], "*", 1)==0)) && (argc==1)) { + i=0; + cli_print(cli, "Stopping "); + do { + if (mops_destroy_thread (cur)==0) { + i++; + cli_print(cli, "[%i] %s", cur->id, cur->packet_name); + } + cur = cur->next; + } + while (head != cur); + cli_print(cli, "\n"); + if (i) { + cli_print(cli, "Stopped %i transmission processe(s)\r", i); + } + else { + cli_print(cli, "No active transmission processes found.\r"); + } + + i = stop_all_sequences (); + if (i) { + cli_print(cli, "Stopped %i sequence(s)\n", i); + } + else { + cli_print(cli, "No active sequences found.\n"); + } + + return CLI_OK; + } + + // Stop all specified packets: + // + for (i=0; i<argc; i++) { + mp = NULL; + // is argv[i] a numerical pkt-id? + if (mz_strisnum(argv[i])) { + slot = (int) str2int(argv[i]); + mp = mops_search_id (mp_head, slot); + } + // still not found? Is it a name? + if (mp==NULL) mp = mops_search_name (mp_head, argv[i]); + if (mp==NULL) cli_print(cli, "Packet '%s' not in list!\r",argv[i]); + else { // packet found: + if (mops_destroy_thread (mp)) { + cli_print(cli, "Packet [%i] '%s' has no associated transmission process (nothing to stop).\r", mp->id, mp->packet_name); + } else + cli_print (cli, "Stopped transission process for packet [%i] '%s'.\r", mp->id, mp->packet_name); + } + } + + cli_print(cli, "\r"); + return CLI_OK; +} + + + +int show_mops(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + char tmp[120]; + + if (strncmp(argv[argc-1], "?", 2)==0) { + cli_print(cli, "<ENTER> Check MOPS version and details\n"); + return CLI_OK; + } + + cli_print(cli, "-----------------------------------------------------\r"); + cli_print(cli, "Mops version %s [%s]\n", MOPS_VERSION, MOPS_CODENAME); + cli_print(cli, "Maximum packet sequence length is %i packets\r", MAX_PACKET_SEQUENCE_LEN); + cli_print(cli, "Maximum frame size is %i bytes\r", MAX_MOPS_FRAME_SIZE); + cli_print(cli, "Minimum frame size is %i bytes\r", MIN_MOPS_FRAME_SIZE); + cli_print(cli, "PCAP readout delay is %i msec\r", PCAP_READ_TIMEOUT_MSEC); + cli_print(cli, "Maximum payload size is %i bytes\r", MAX_MOPS_MSG_SIZE); + cli_print(cli, "Maximum chunk size is %i bytes\r", MAX_MOPS_MSG_CHUNK_SIZE); + cli_print(cli, "Maximum counters per packet is %i\r", MAX_MOPS_COUNTERS_PER_PACKET); + cli_print(cli, "Maximum number of 802.1Q tags is %i\r", MAX_MOPS_DOT1Q_TAGS); + cli_print(cli, "Maximum number of MPLS tags is %i\r", MAX_MOPS_MPLS_TAGS); + cli_print(cli, "Maximum length of packet names is %i characters\r", MAX_MOPS_PACKET_NAME_LEN); + cli_print(cli, "Maximum length of packet descriptions is %i characters\r", MAX_MOPS_DESCRIPTION_LEN); + cli_print(cli, "Bytes per line for formatted frame output %i\r", MAX_CLI_LINE_BYTES); + cli_print(cli, "Maximum LLDP optional section length is %i bytes\r", MAX_LLDP_OPT_TLVS); + if (AUTOMOPS_ENABLED) { + cli_print(cli, "Auto-MOPS subsystem is enabled\r"); + cli_print(cli, " Maximum nesting depth is %i\r", XN_MAX_STACK); + cli_print(cli, " Maximum file size for protocol definitions is %i\r", AUTOMOPS_MAX_FILE_SIZE); + cli_print(cli, " Maximum names length is %i\r", AUTOMOPS_MAX_NAME_LEN); + cli_print(cli, " Maximum short description length is %i\r", AUTOMOPS_MAX_SHORTDESC_LEN); + cli_print(cli, " Maximum XML tag length is %i\r", XML_MAX_TAG_LEN); + } else cli_print(cli, "Auto-MOPS subsystem is disabled\r"); + + if (mops_dump_all(mp_head, tmp)) { + cli_print(cli, "No mopses found.\n"); // keine Möpse gefunden ;-) + } else { + cli_print(cli, "%s\n", tmp); + } + + return CLI_OK; +} + + + + +int cmd_reset_interface (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + int i; + + if (strncmp(argv[argc-1], "?", 2)==0) { + cli_print(cli, "<ENTER> Check MOPS version and details\n"); + return CLI_OK; + } + + if (argc>0) { + cli_print(cli, "Unknown parameter\n"); + return CLI_OK; + } + + lookupdev(); + + for (i=0; i<device_list_entries; i++) { + get_dev_params(device_list[i].dev); + // refresh ARP table i. e. MAC addresses of default GWs + service_arp(device_list[i].dev, device_list[i].ip_gw, device_list[i].mac_gw); + } + + return CLI_OK; +} + + + + +int conf_frame_limit (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + unsigned int tmp; + + if (strncmp(argv[argc-1], "?", 2)==0) + { + cli_print(cli, "Configure global frame size limits:\n"); + cli_print(cli, " <min-frame-size> [max-frame-size]\n"); + return CLI_OK; + } + + if (argc>2) + { + cli_print(cli, "Two arguments allowed: <min-frame-size> [max-frame-size]\n"); + return CLI_OK; + } + + tmp = (unsigned int) str2int (argv[0]); + if (tmp < MIN_MOPS_FRAME_SIZE) + { + cli_print(cli, "This Mausezahn requires that the minimum frame size is at least %i bytes\n", MIN_MOPS_FRAME_SIZE); + return CLI_OK; + } + + if (tmp>(max_frame_s-2)) + { + cli_print(cli, "The minimum frame size must be below %i bytes\n", max_frame_s-1); + return CLI_OK; + } + + min_frame_s = tmp; + + if (argc==2) + { + tmp = (unsigned int) str2int (argv[1]); + + if (tmp > MAX_MOPS_FRAME_SIZE-MOPS_SIZE_MARGIN) + { + cli_print(cli, "This Mausezahn requires that the maximum frame size is not greater than %i bytes\n", + MAX_MOPS_FRAME_SIZE-MOPS_SIZE_MARGIN); + return CLI_OK; + } + + if (tmp<(min_frame_s+2)) + { + cli_print(cli, "The maximum frame size must be greater than %i bytes\n", min_frame_s+1); + return CLI_OK; + } + + max_frame_s = tmp; + } + + return CLI_OK; +} + + + + +int cmd_load (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + int i; + FILE *fp; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc!=1) ) { + cli_print(cli, "Load commands from one or more specified file(s)\r"); + cli_print(cli, "\n"); + return CLI_OK; + } + + if (!argc){ + cli_print(cli, "Specify one or more configuration files\n"); + return CLI_OK; + } + + for (i=0; i<argc; i++) { + fp = fopen(argv[i], "r"); + if (fp==NULL) { + cli_print(cli, "Warning: Cannot read %s\n", argv[i]); + continue; + } + cli_print(cli, "Read commands from %s...\n", argv[i]); + cli_file (cli, fp, PRIVILEGE_PRIVILEGED, MODE_EXEC); + if (fclose(fp) == EOF) + { + cli_print(cli, "Warning: problems closing %s (errno=%i)\n", argv[i],errno); + } + } + + return CLI_OK; +} + + +int show_arp (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + int i; + struct arp_table_struct *cur; + char s[128], ip[20], uc[16], bc[16], ch[16]; + struct mz_timestamp now, prev, result; + + + + if (strcmp(argv[argc-1],"?")==0) { + cli_print(cli, "<CR> shows the advanced Mausezahn ARP table\n"); + return CLI_OK; + } + + if (argc>0) { + cli_print(cli, "Unknown parameter\n"); + return CLI_OK; + } + + + cli_print(cli, "Intf Index IP address MAC address last Ch UCast BCast Info\r"); + cli_print(cli, "----------------------------------------------------------------------------------\r"); +// ------------------------------------------------------------------------------ +// wlan0 [1] DL 192.168.0.1 at 00:09:5b:9a:15:84 3'42'' 1 + + for (i=0; i<device_list_entries; i++) { + cur=device_list[i].arp_table; + while(cur!=NULL) { + sprintf(ip,"%i.%i.%i.%i",cur->sip[0],cur->sip[1],cur->sip[2],cur->sip[3]); + if (cur->changed>99999) mz_strncpy(ch,"ALERT",6); else sprintf(ch,"%lu", cur->changed); + if (cur->uni_resp>99999) mz_strncpy(uc,"ALERT",6); else sprintf(uc,"%lu", cur->uni_resp); + if (cur->bc_resp>99999) mz_strncpy(bc,"ALERT",6); else sprintf(bc,"%lu", cur->bc_resp); + sprintf(s, "%-7s [%i] %s%s %15s %02x:%02x:%02x:%02x:%02x:%02x %8s %5s %5s %5s %04x", + device_list[i].dev, + cur->index, + (cur->dynamic) ? "D" : "U", + (cur->locked) ? "L" : "", + ip, + cur->smac[0], + cur->smac[1], + cur->smac[2], + cur->smac[3], + cur->smac[4], + cur->smac[5], + cur->when, + ch, + uc, + bc, + cur->flags); + cli_print(cli, "%s\r", s); + if (cur->changed>1) { + now.sec = cur->sec; + now.nsec = cur->nsec; + prev.sec = cur->sec_prev; + prev.nsec= cur->nsec_prev; + printf("sec=%u nsec=%u sec=%u nsec=%u\n", cur->sec, cur->nsec, cur->sec_prev, cur->nsec_prev); + timestamp_subtract(&now, &prev, &result); + sprintf(s," previous MAC was: %02x:%02x:%02x:%02x:%02x:%02x time delta: %u sec %u msec", + cur->smac_prev[0], + cur->smac_prev[1], + cur->smac_prev[2], + cur->smac_prev[3], + cur->smac_prev[4], + cur->smac_prev[5], + (unsigned int) result.sec, (unsigned int) result.nsec/1000000); + cli_print(cli, " %s\r", s); + } + cur=cur->next; + } + + } + return CLI_OK; +} + + +// general 'end' command to return to global config mode +int cmd_end_to_config(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + cli_set_configmode(cli, MODE_CONFIG, NULL); + return CLI_OK; +} diff --git a/staging/cli_dns.c b/staging/cli_dns.c new file mode 100644 index 0000000..be1e7ea --- /dev/null +++ b/staging/cli_dns.c @@ -0,0 +1,53 @@ +/* + * 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 + * +*/ + + + +#include "mz.h" +#include "cli.h" +#include "mops.h" + +int cmd_dns_query(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + + return CLI_OK; +} + + +int cmd_dns_answer(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + + return CLI_OK; +} + + +int cmd_dns_ttl(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + + return CLI_OK; +} + + +int cmd_dns_end(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + char prompt[16]; + sprintf(prompt, "pkt-%i",clipkt->id); + cli_set_configmode(cli, MZ_MODE_PACKET, prompt); + return CLI_OK; +} + diff --git a/staging/cli_eth.c b/staging/cli_eth.c new file mode 100644 index 0000000..668aa95 --- /dev/null +++ b/staging/cli_eth.c @@ -0,0 +1,269 @@ +/* + * 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 + * +*/ + + +#include "mz.h" +#include "cli.h" +#include "mops.h" + + + + + +int cmd_packet_mac_address_source (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + int i,j; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>1) ) + { + cli_print(cli, "XX:XX:XX:XX:XX:XX Configure a source MAC address\n"); + cli_print(cli, "Optionally you may use randomly generated (unicast)\r"); + cli_print(cli, "MAC addresses, using the keyword 'random'\n"); + return CLI_OK; + } + + if (argc==1) + { + if (mz_strcmp(argv[0], "random", 3)==0) + { + clipkt->eth_src_israndom = 1; + return CLI_OK; + } + + if (mz_strcmp(argv[0], "default", 3)==0) + { + // find index of device_list with the device configured in clipkt: + i=0; + while (strncmp(device_list[i].dev, clipkt->device, 10) && (i<device_list_entries)) i++; + for (j=0;j<6;j++) clipkt->eth_src[j] = device_list[i].mac_mops[j]; + clipkt->eth_src_israndom = 0; + return CLI_OK; + } + + if (mops_pdesc_mac(clipkt->eth_src, argv[0])) + { + cli_print(cli,"Invalid MAC address (use format: XX:XX:XX:XX:XX:XX)\n"); + } + else // MAC was OK + { + clipkt->eth_src_israndom = 0; + } + } + else + cli_print(cli, "Invalid MAC format!\n"); + + + return CLI_OK; +} + + + +int cmd_packet_mac_address_destination (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>1) ) + { + cli_print(cli, "XX:XX:XX:XX:XX:XX Configure a destination MAC address\n"); + return CLI_OK; + } + if (argc==1) + { + if (mz_strcmp(argv[0], "bcast", 2)==0) + { + mops_pdesc_mac (clipkt->eth_dst, "ff:ff:ff:ff:ff:ff"); + return CLI_OK; + } + else if (mz_strcmp(argv[0], "pvst", 2)==0) + { + mops_pdesc_mac (clipkt->eth_dst, "01:00:0C:CC:CC:CD"); + return CLI_OK; + } + else if (mz_strcmp(argv[0], "cisco", 2)==0) + { + mops_pdesc_mac (clipkt->eth_dst, "01:00:0C:CC:CC:CC"); + return CLI_OK; + } + else if (mz_strcmp(argv[0], "stp", 2)==0) + { + mops_pdesc_mac (clipkt->eth_dst, "01:80:C2:00:00:00"); + return CLI_OK; + } + + if (mops_pdesc_mac(clipkt->eth_dst, argv[0])) + { + cli_print(cli,"Invalid MAC address (use format: XX:XX:XX:XX:XX:XX)\n"); + } + } + else + cli_print(cli, "Invalid MAC format!\n"); + + return CLI_OK; +} + + + + + + +int cmd_eth_type (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + unsigned long int t32; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>1) ) + { + cli_print(cli, "Specify the Ethernet type field in hexadecimal format.\n"); + cli_print(cli, "For example:\n"); + cli_print(cli, " 800 ......... IP\r"); + cli_print(cli, " 806 ......... ARP\r"); + cli_print(cli, " 835 ......... RARP\r"); + cli_print(cli, " 8100 ......... 802.1Q\r"); + cli_print(cli, " 888E ......... 802.1X\r"); + cli_print(cli, "\n"); + return CLI_OK; + } + + if (argc==1) + { + t32 = xstr2int(argv[0]); + if (t32>0xffff) + { + cli_print(cli, "EtherType must not exceed ffff.\n"); + return CLI_OK; + } + if (t32<0x800) + { + cli_print(cli, "WARNING: 'Officially' the EtherType must be greater or equal 800.\n"); + } + + clipkt->eth_type = (u_int16_t) t32; + } + else + { + cli_print(cli, "Only one parameter accepted.\n"); + } + + return CLI_OK; +} + + + + +int cmd_eth_length (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + unsigned long int t32; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>1) ) + { + cli_print(cli, "Specify the 802.3 length field in decimal notation.\r"); + cli_print(cli, "\n"); + return CLI_OK; + } + + if (argc==1) + { + t32 = str2int(argv[0]); + if (t32>0xffff) + { + cli_print(cli, "The length field must not exceed 65535.\n"); + return CLI_OK; + } + if (t32>0x7ff) + { + cli_print(cli, "WARNING: 'Officially' the 802.3 length field must not be greater than 1522.\n"); + } + + clipkt->eth_len = (u_int16_t) t32; + } + else + { + cli_print(cli, "Only one parameter accepted.\n"); + } + + + + return CLI_OK; +} + + + + + +int cmd_eth_llc (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>1) ) + { + cli_print(cli, "Specify the IEEE 802.2 Logical Link Control (LLC) in hexadecimal format.\n"); + return CLI_OK; + } + + // DSAP-SSAP-Ctrl + // ***** TODO ***** + cli_print(cli, "Not supported in this version.\n"); + + return CLI_OK; +} + + + + +int cmd_eth_snap (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + + u_int8_t + oui[16], + etp[16], + t8[16] = {0xAA, 0xAA, 0x03}; + + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>1) ) + { + cli_print(cli, "Specify the SNAP header (OUI+Type) in hexadecimal format\r"); + cli_print(cli, "Example: 00:00:0e 08:00\r"); + cli_print(cli, "\n"); + return CLI_OK; + } + + if (argc!=2) + { + cli_print(cli, "Two arguments required: 3-byte OUI and 2-byte EtherType\n"); + return CLI_OK; + } + + if (str2hex(argv[0], oui, 15)!=3) + { + cli_print(cli, "Three bytes required for the OUI\n"); + return CLI_OK; + } + + if (str2hex(argv[1], etp, 15)!=2) + { + cli_print(cli, "Two bytes required for the EtherType\n"); + return CLI_OK; + } + + + memcpy(&clipkt->eth_snap[0], &t8, 3); + memcpy(&clipkt->eth_snap[3], &oui, 3); + memcpy(&clipkt->eth_snap[6], &etp, 2); + clipkt->eth_snap_s = 8; + + + + return CLI_OK; +} diff --git a/staging/cli_igmp.c b/staging/cli_igmp.c new file mode 100644 index 0000000..cdd1df1 --- /dev/null +++ b/staging/cli_igmp.c @@ -0,0 +1,322 @@ +/* + * 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 + * +*/ + + + +#include "mz.h" +#include "cli.h" +#include "mops.h" + + +int cmd_igmpv2_genquery (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + int mrt, sum; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>2) ) { + cli_print(cli, "Configure a IGMPv2 general query.\n"); + cli_print(cli, "ARGUMENTS: [<MRT> [<checksum>]]\n"); + cli_print(cli, "<MRT> ... maximum response time in 100 msec units (default: 10 s)\r"); + cli_print(cli, "<checksum> ... user-defined checksum (usually wrong by intention) in \r"); + cli_print(cli, " hexadecimal notation (e. g. 'c7b3').\n"); + return CLI_OK; + } + + if (argc>=1) { + if (mz_strisnum(argv[0])==0) { + cli_print(cli, "Maximum response time must only contain numbers!\n"); + return CLI_OK; + } + mrt = (int) str2int(argv[0]); + } else mrt = 100; // default: 10 s + + if (argc==2) { + if (mz_strishex(argv[1])==0) { + cli_print(cli, "Checksum must only contain hexadecimal numbers!\n"); + return CLI_OK; + } + sum = (int) xstr2int(argv[1]); + if (sum>0xffff) { + cli_print(cli, "Checksum must be a 2-byte value!\n"); + return CLI_OK; + } + + } else sum = -1; + + clipkt->ip_dst = str2ip32("224.0.0.1"); + clipkt->ip_ttl = 1; + clipkt->ndelay.tv_sec = 125; + clipkt->ndelay.tv_nsec = 0; + if (mops_create_igmpv2 (clipkt, 0, IGMP_GENERAL_QUERY, mrt, sum, 0)) + cli_print(cli, "Invalid parameters!\n"); + + return CLI_OK; +} + + +int cmd_igmpv2_specquery (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + int mrt=100, sum=-1; + u_int8_t IP[4]; + u_int32_t mip=0; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>3) ) { + cli_print(cli, "Configure a IGMPv2 group-specific query.\n"); + cli_print(cli, "ARGUMENTS: <IP-address> [<MRT> [<checksum>]]\n"); + cli_print(cli, "<IP-Address> ... multicast group to be queried (can be ANY IP address!)\r"); + cli_print(cli, "<MRT> ... maximum response time in 100 msec units (default: 10 s)\r"); + cli_print(cli, "<checksum> ... user-defined checksum (usually wrong by intention) in \r"); + cli_print(cli, " hexadecimal notation (e. g. 'c7b3').\n"); + return CLI_OK; + } + + + if (argc==0) { + cli_print(cli, "You must at least specify the group address\n"); + return CLI_OK; + } + + if (argc>=1) { + if (mops_pdesc_ip (IP, argv[0])==0) // check if format is really an IP address + mip = str2ip32(argv[0]); + else { + cli_print(cli, "Invalid IP address\n"); + return CLI_OK; + } + } + + if (argc>=2) { + if (mz_strisnum(argv[1])==0) { + cli_print(cli, "Maximum response time must only contain numbers!\n"); + return CLI_OK; + } + mrt = (int) str2int(argv[1]); + } + + if (argc==3) { + if (mz_strishex(argv[2])==0) { + cli_print(cli, "Checksum must only contain hexadecimal numbers!\n"); + return CLI_OK; + } + sum = (int) xstr2int(argv[2]); + if (sum>0xffff) { + cli_print(cli, "Checksum must be a 2-byte value!\n"); + return CLI_OK; + } + } + + clipkt->ip_dst = mip; + clipkt->ip_ttl = 1; + clipkt->ndelay.tv_sec = 125; + clipkt->ndelay.tv_nsec = 0; + if (mops_create_igmpv2 (clipkt, 0, IGMP_GSPEC_QUERY, mrt, sum, mip)) + cli_print(cli, "Invalid parameters!\n"); + + return CLI_OK; +} + + + + + +int cmd_igmpv2_report (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + int sum; + u_int8_t IP[4]; + u_int32_t mip=0; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>2) || (argc==0)) { + cli_print(cli, "Configure a IGMPv2 membership report.\n"); + cli_print(cli, "ARGUMENTS: <IP-Address> [<checksum>]\n"); + cli_print(cli, "<IP-Address> ... multicast group address to be reported (but ANY IP\r"); + cli_print(cli, " address allowed, Mausezahn is really generous...)\r"); + cli_print(cli, "<checksum> ... user-defined checksum (usually wrong by intention) in \r"); + cli_print(cli, " hexadecimal notation (e. g. 'c7b3').\n"); + return CLI_OK; + } + + + if (argc>=1) { + if (mops_pdesc_ip (IP, argv[0])==0) // check if format is really an IP address + mip = str2ip32(argv[0]); + else { + cli_print(cli, "Invalid IP address\n"); + return CLI_OK; + } + } + + if (argc==2) { + if (mz_strishex(argv[1])==0) { + cli_print(cli, "Checksum must only contain hexadecimal numbers!\n"); + return CLI_OK; + } + sum = (int) xstr2int(argv[1]); + if (sum>0xffff) { + cli_print(cli, "Checksum must be a 2-byte value!\n"); + return CLI_OK; + } + } else sum = -1; + + clipkt->ip_dst = mip; + clipkt->ip_ttl = 1; + clipkt->ndelay.tv_sec = 1; + clipkt->ndelay.tv_nsec = 0; + + if (mops_create_igmpv2 (clipkt, 0, IGMP_V2_REPORT, 0, sum, mip)) + cli_print(cli, "Invalid parameters!\n"); + + return CLI_OK; +} + + +int cmd_igmpv2_leave (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + int sum; + u_int8_t IP[4]; + u_int32_t mip=0; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>2) || (argc==0)) { + cli_print(cli, "Configure a IGMPv2 leave group message.\n"); + cli_print(cli, "ARGUMENTS: <IP-Address> [<checksum>]\n"); + cli_print(cli, "<IP-Address> ... multicast group address that should be left; use\r"); + cli_print(cli, " the special address 0.0.0.0 for a 'general leave'\r"); + cli_print(cli, "<checksum> ... user-defined checksum (usually wrong by intention) in \r"); + cli_print(cli, " hexadecimal notation (e. g. 'c7b3').\n"); + return CLI_OK; + } + + + if (argc>=1) { + if (mops_pdesc_ip (IP, argv[0])==0) // check if format is really an IP address + mip = str2ip32(argv[0]); + else { + cli_print(cli, "Invalid IP address\n"); + return CLI_OK; + } + } + + if (argc==2) { + if (mz_strishex(argv[1])==0) { + cli_print(cli, "Checksum must only contain hexadecimal numbers!\n"); + return CLI_OK; + } + sum = (int) xstr2int(argv[1]); + if (sum>0xffff) { + cli_print(cli, "Checksum must be a 2-byte value!\n"); + return CLI_OK; + } + } else sum = -1; + + clipkt->ip_dst = str2ip32("224.0.0.2"); + clipkt->ip_ttl = 1; + clipkt->ndelay.tv_sec = 1; + clipkt->ndelay.tv_nsec = 0; + + if (mops_create_igmpv2 (clipkt, 0, IGMP_LEAVE, 0, sum, mip)) + cli_print(cli, "Invalid parameters!\n"); + + return CLI_OK; +} + + + + + +int cmd_igmpv1_query (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + int sum; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>1) ) { + cli_print(cli, "Configure a IGMPv1 query.\n"); + cli_print(cli, "OPTIONAL ARGUMENT: [<checksum>]\n"); + cli_print(cli, "<checksum> ... user-defined checksum (usually wrong by intention) in \r"); + cli_print(cli, " hexadecimal notation (e. g. 'c7b3').\n"); + return CLI_OK; + } + + if (argc==1) { + if (mz_strishex(argv[0])==0) { + cli_print(cli, "Checksum must only contain hexadecimal numbers!\n"); + return CLI_OK; + } + sum = (int) xstr2int(argv[0]); + if (sum>0xffff) { + cli_print(cli, "Checksum must be a 2-byte value!\n"); + return CLI_OK; + } + } else sum = -1; + + clipkt->ip_dst = str2ip32("224.0.0.1"); + clipkt->ip_ttl = 1; + clipkt->ndelay.tv_sec = 125; + clipkt->ndelay.tv_nsec = 0; + if (mops_create_igmpv2 (clipkt, 0, IGMP_GENERAL_QUERY, 0, sum, 0)) + cli_print(cli, "Invalid parameters!\n"); + + return CLI_OK; +} + + +int cmd_igmpv1_report (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + int sum; + u_int8_t IP[4]; + u_int32_t mip=0; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>2) || (argc==0)) { + cli_print(cli, "Configure a IGMPv1 membership report.\n"); + cli_print(cli, "ARGUMENTS: <IP-Address> [<checksum>]\n"); + cli_print(cli, "<IP-Address> ... multicast group address to be reported (but ANY IP\r"); + cli_print(cli, " address allowed, Mausezahn is really generous...)\r"); + cli_print(cli, "<checksum> ... user-defined checksum (usually wrong by intention) in \r"); + cli_print(cli, " hexadecimal notation (e. g. 'c7b3').\n"); + return CLI_OK; + } + + + if (argc>=1) { + if (mops_pdesc_ip (IP, argv[0])==0) // check if format is really an IP address + mip = str2ip32(argv[0]); + else { + cli_print(cli, "Invalid IP address\n"); + return CLI_OK; + } + } + + if (argc==2) { + if (mz_strishex(argv[1])==0) { + cli_print(cli, "Checksum must only contain hexadecimal numbers!\n"); + return CLI_OK; + } + sum = (int) xstr2int(argv[1]); + if (sum>0xffff) { + cli_print(cli, "Checksum must be a 2-byte value!\n"); + return CLI_OK; + } + } else sum = -1; + + clipkt->ip_dst = mip; + clipkt->ip_ttl = 1; + clipkt->ndelay.tv_sec = 1; + clipkt->ndelay.tv_nsec = 0; + + if (mops_create_igmpv2 (clipkt, 0, IGMP_V1_REPORT, 0, sum, mip)) + cli_print(cli, "Invalid parameters!\n"); + + return CLI_OK; +} + diff --git a/staging/cli_interface.c b/staging/cli_interface.c new file mode 100644 index 0000000..65cb46d --- /dev/null +++ b/staging/cli_interface.c @@ -0,0 +1,142 @@ +/* + * 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 + * +*/ + + +#include "mz.h" +#include "cli.h" +#include "mops.h" + + + + +// Enter interface config mode: +// +int enter_interface (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + int i, j=0; + char prompt[10]; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>1) ) + { + cli_print(cli, "Specify an interface to configure\n"); + return CLI_OK; + } + + if (argc) + { + for (i=0; i<device_list_entries; i++) + { + if (strncmp(device_list[i].dev, argv[0], 16)==0) + { + j=1; + sprintf(prompt, "if-%s", device_list[i].dev); + clidev = i; + break; + } + } + + if (j) + { + cli_set_configmode(cli, MZ_MODE_INTERFACE, prompt); + } + else + { + cli_print(cli, "Unknown device!\n"); + } + } + else + { + cli_print(cli, "Specify an interface to configure\n"); + } + + return CLI_OK; +} + + + +int conf_ip_address (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + if ( (strcmp(argv[argc-1],"?")==0) || (argc>1) ) + { + cli_print(cli, "A.B.C.D Specify a default interface IP address\n"); + return CLI_OK; + } + + if (argc) + { + if (mops_pdesc_ip (device_list[clidev].ip_mops, argv[0])) + { + cli_print(cli,"Invalid IP address (use format: A.B.C.D)\n"); + } + } + else + cli_print(cli, "A.B.C.D Specify a default interface IP address\n"); + + return CLI_OK; +} + + + +int conf_mac_address (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + if ( (strcmp(argv[argc-1],"?")==0) || (argc>1) ) + { + cli_print(cli, "XX:XX:XX:XX:XX:XX Configure a default interface MAC address\n"); + return CLI_OK; + } + + if (argc) + { + if (mops_pdesc_mac (device_list[clidev].mac_mops, argv[0])) + { + cli_print(cli,"Invalid MAC address (use format: XX:XX:XX:XX:XX:XX)\n"); + } + } + else + cli_print(cli, "A.B.C.D Specify a default interface IP address\n"); + + return CLI_OK; +} + + + +int conf_tag_dot1q (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + if ( (strcmp(argv[argc-1],"?")==0) || (argc>1) ) + { + cli_print(cli, "Specify one or more 802.1Q (and optionally 802.1P) tags\n"); + return CLI_OK; + } + cli_print(cli, "Not supported in this version\n"); + return CLI_OK; +} + +int conf_tag_mpls (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + if ( (strcmp(argv[argc-1],"?")==0) || (argc>1) ) + { + cli_print(cli, "Specify one or more MPLS labels (and parameters)\n"); + return CLI_OK; + } + cli_print(cli, "Not supported in this version\n"); + return CLI_OK; +} + + + + diff --git a/staging/cli_ip.c b/staging/cli_ip.c new file mode 100644 index 0000000..55f5683 --- /dev/null +++ b/staging/cli_ip.c @@ -0,0 +1,888 @@ +/* + * 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 + * +*/ + + + +#include "mz.h" +#include "cli.h" +#include "mops.h" + + +// ------- TOC --------- +// +// int cmd_ip_address_source (struct cli_def *cli, char *command, char *argv[], int argc) +// int cmd_ip_address_destination (struct cli_def *cli, char *command, char *argv[], int argc) +// int cmd_ip_version (struct cli_def *cli, char *command, char *argv[], int argc) +// int cmd_ip_ttl (struct cli_def *cli, char *command, char *argv[], int argc) +// int cmd_ip_protocol (struct cli_def *cli, char *command, char *argv[], int argc) +// int cmd_ip_hlen (struct cli_def *cli, char *command, char *argv[], int argc) +// int cmd_ip_len (struct cli_def *cli, char *command, char *argv[], int argc) +// int cmd_ip_id (struct cli_def *cli, char *command, char *argv[], int argc) +// int cmd_ip_offset (struct cli_def *cli, char *command, char *argv[], int argc) +// int cmd_ip_sum (struct cli_def *cli, char *command, char *argv[], int argc) +// int cmd_ip_tos (struct cli_def *cli, char *command, char *argv[], int argc) +// int cmd_ip_dscp (struct cli_def *cli, char *command, char *argv[], int argc) +// int cmd_ip_rsv (struct cli_def *cli, char *command, char *argv[], int argc) +// int cmd_ip_df (struct cli_def *cli, char *command, char *argv[], int argc) +// int cmd_ip_mf (struct cli_def *cli, char *command, char *argv[], int argc) +// int cmd_ip_option (struct cli_def *cli, char *command, char *argv[], int argc) + + + +// ip-address source default|<IP>|rand|range +// +// default +// random +// A.B.C.D +// A.B.C.D /24 +// A.B.C.D E.F.G.H +int cmd_ip_address_source (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + u_int8_t IP1[4], IP2[4]; + u_int32_t ip1, ip2; + unsigned int prefix; + u_int32_t mask, invmask; + int i,r; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>2) ) + { + cli_print(cli, "A.B.C.D configure a source IP address\n"); + cli_print(cli, "Optionally you may specify\r"); + cli_print(cli, "- a range of addresses, such as: 192.168.0.0 /16\r"); + cli_print(cli, " or: 192.168.0.1 192.168.255.255\r"); + cli_print(cli, "- 'random' for a randomly generated source address\r"); + cli_print(cli, "- 'default' for the interface default settings\n"); + return CLI_OK; + } + + switch (argc) + { + case 1: + if (mz_strcmp(argv[0], "default", 3)==0) + { + // find index of device_list with the device configured in clipkt: + i=0; + while (strncmp(device_list[i].dev, clipkt->device, 10) && (i<device_list_entries)) i++; + clipkt->ip_src = device_list[i].ip_mops[3] + + device_list[i].ip_mops[2] * 256 + + device_list[i].ip_mops[1] * 256 * 256 + + device_list[i].ip_mops[0] * 256 * 256 * 256; + clipkt->ip_src_israndom = 0; + clipkt->ip_src_isrange = 0; + } + else if (mz_strcmp(argv[0], "random", 3)==0) + { + clipkt->ip_src_israndom = 1; + clipkt->ip_src_isrange = 0; + } + else if (mops_pdesc_ip (IP1, argv[0])==0) // check if format is really an IP address + { + clipkt->ip_src = str2ip32(argv[0]); + clipkt->ip_src_israndom = 0; + clipkt->ip_src_isrange = 0; + } + else // wrong input + { + cli_print(cli,"Invalid address/keyword\n"); + } + break; + case 2: // MUST be either like '10.1.1.0 /24' or '10.1.1.1 10.1.1.254' + if (mops_pdesc_ip (IP1, argv[0])==0) // check if format is really an IP address + { + clipkt->ip_src_start = str2ip32(argv[0]); + if (strlen(argv[1])<4) // probably prefix? + { + r=sscanf(argv[1],"/%u",&prefix); + if ((r==EOF) || (r==0) || (prefix<1) || (prefix>31)) + cli_print(cli, "Invalid prefix!\n"); + else + { + mask = 0xffffffff; + mask <<= (32-prefix); + invmask = 0xffffffff - mask; + ip1 = ((str2ip32 (argv[0])) & mask) +1; // the '+1' is to ensure that we do not start with the net-id + ip2 = ip1 | invmask; + clipkt->ip_src_start = ip1; + clipkt->ip_src_stop = ip2; + clipkt->ip_src_isrange = 1; + clipkt->ip_src_israndom = 0; + } + } + else if (mops_pdesc_ip (IP2, argv[1])==0) // probably 2nd IP address? + { + if (str2ip32(argv[1]) > clipkt->ip_src_start) + { + clipkt->ip_src_stop = str2ip32(argv[1]); + clipkt->ip_src_isrange = 1; + clipkt->ip_src_israndom = 0; + } + else + { + cli_print(cli, "Invalid range! The second IP address must be greater than the first!\n"); + } + } + else + { + cli_print(cli, "Second parameter must be either a valid IP address or a prefix length \n"); + } + } + else // first string is not a valid IP address + { + cli_print(cli, "First parameter must be a valid IP address\n"); + } + break; + default: + cli_print(cli, "Invalid format!\n"); + } + + return CLI_OK; +} + + + +// ip-address destination <IP>|range +int cmd_ip_address_destination (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + u_int8_t IP1[4], IP2[4]; + u_int32_t ip1, ip2; + unsigned int prefix; + u_int32_t mask, invmask; + int r; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>2) ) + { + cli_print(cli, "A.B.C.D configure a destination IP address\n"); + cli_print(cli, "Optionally specify a range of addresses, such as: 192.168.0.0 /16\r"); + cli_print(cli, " or: 192.168.0.1 192.168.255.255\n"); + return CLI_OK; + } + + switch (argc) + { + case 1: + if (mops_pdesc_ip (IP1, argv[0])==0) // check if format is really an IP address + { + clipkt->ip_dst = str2ip32(argv[0]); + clipkt->ip_dst_isrange = 0; + } + else // wrong input + { + cli_print(cli,"Invalid address/range\n"); + } + break; + case 2: // MUST be either like '10.1.1.0 /24' or '10.1.1.1 10.1.1.254' + if (mops_pdesc_ip (IP1, argv[0])==0) // check if format is really an IP address + { + clipkt->ip_dst_start = str2ip32(argv[0]); + if (strlen(argv[1])<4) // probably prefix? + { + r=sscanf(argv[1],"/%u",&prefix); + if ((r==EOF) || (r==0) || (prefix<1) || (prefix>31)) + cli_print(cli, "Invalid prefix!\n"); + else + { + mask = 0xffffffff; + mask <<= (32-prefix); + invmask = 0xffffffff - mask; + ip1 = ((str2ip32 (argv[0])) & mask) +1; // the '+1' is to ensure that we do not start with the net-id + ip2 = ip1 | invmask; + clipkt->ip_dst_start = ip1; + clipkt->ip_dst_stop = ip2; + clipkt->ip_dst_isrange = 1; + } + } + else if (mops_pdesc_ip (IP2, argv[1])==0) // probably 2nd IP address? + { + if (str2ip32(argv[1]) > clipkt->ip_dst_start) + { + clipkt->ip_dst_stop = str2ip32(argv[1]); + clipkt->ip_dst_isrange = 1; + } + else + { + cli_print(cli, "Range requirement: The second IP address must be greater than the first!\n"); + } + } + else + { + cli_print(cli, "Second parameter must be either a valid IP address or a prefix length \n"); + } + } + else // first string is not a valid IP address + { + cli_print(cli, "First parameter must be a valid IP address\n"); + } + break; + default: + cli_print(cli, "Invalid IP or range specification!\n"); + } + + return CLI_OK; +} + + + + +int cmd_ip_version (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + int ver; + + if (strncmp(argv[argc-1], "?", 2)==0) + { + cli_print(cli, "Specify the IP version (default: 4).\n"); + return CLI_OK; + } + + ver = (int) str2int(argv[0]); + + if (ver>15) + { + cli_print(cli, "Version must be within range 0..15\n"); + return CLI_OK; + } + + clipkt->ip_version = ver; + + return CLI_OK; +} + + + +int cmd_ip_ttl (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + int ttl; + + if (strncmp(argv[argc-1], "?", 2)==0) + { + cli_print(cli, "Specify the TTL (default: 255).\n"); + + return CLI_OK; + } + + ttl = (int) str2int(argv[0]); + + if (ttl>255) + { + cli_print(cli, "TTL must be within range 0..255\n"); + return CLI_OK; + } + + clipkt->ip_ttl = ttl; + + + return CLI_OK; +} + + + +int cmd_ip_protocol (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + int proto; + + if (strncmp(argv[argc-1], "?", 2)==0) + { + cli_print(cli, "Specify the protocol number (default: 0).\n"); + + return CLI_OK; + } + + proto = (int) str2int(argv[0]); + + if (proto>255) + { + cli_print(cli, "The protocol number must be within range 0..255\n"); + return CLI_OK; + } + + clipkt->ip_proto = proto; + + return CLI_OK; +} + + + + + +int cmd_ip_hlen (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + int ihl; + + if (strncmp(argv[argc-1], "?", 2)==0) + { + cli_print(cli, "Specify the header length in multiple of 4 bytes.\n"); + + return CLI_OK; + } + + ihl = (int) str2int(argv[0]); + + if (ihl>15) + { + cli_print(cli, "The IHL must be within range 0..15\n"); + return CLI_OK; + } + + clipkt->ip_IHL = ihl; + + return CLI_OK; +} + + + + + +int cmd_ip_len (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + int len; + + if (strncmp(argv[argc-1], "?", 2)==0) + { + cli_print(cli, "Specify the total packet length (0..65535).\n"); + + return CLI_OK; + } + + len = (int) str2int(argv[0]); + + if (len>65535) + { + cli_print(cli, "The packet length must be within range 0..65535\n"); + return CLI_OK; + } + + clipkt->ip_len = len; + + return CLI_OK; +} + + + + + +int cmd_ip_id (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + + u_int32_t id; + + if (strncmp(argv[argc-1], "?", 2)==0) + { + cli_print(cli, "Specify the packet identification number (0..4294967295).\n"); + return CLI_OK; + } + + if (mz_strcmp(argv[0], "hex", 2)==0) + { + id = xstr2int (argv[1]); + } + else + { + id = str2int (argv[0]); + } + + clipkt->ip_id = id; + + return CLI_OK; +} + + + + + + +int cmd_ip_offset (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + + int offset; + + if (strncmp(argv[argc-1], "?", 2)==0) { + cli_print(cli, "Specify the fragment offset in multiples of 8 bytes.\n"); + return CLI_OK; + } + + offset = (int) str2int(argv[0]); + + if (offset>8191) { + cli_print(cli, "The fragment offset must be within range 0..8191\n"); + return CLI_OK; + } + + clipkt->ip_frag_offset = offset; + + return CLI_OK; +} + + + + + +int cmd_ip_sum (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + int sum; + + if (strncmp(argv[argc-1], "?", 2)==0) + { + cli_print(cli, "Specify the IP checksum in hexadecimal or use the keyword 'auto'.\r"); + cli_print(cli, "By default, the checksum is computed automatically.\n"); + return CLI_OK; + } + + if (mz_strcmp(argv[0], "auto", 2)==0) + { + clipkt->ip_sum_false=0; + return CLI_OK; + } + + sum = (int) xstr2int(argv[0]); + + if (sum>0xffff) + { + cli_print(cli, "The checksum must be within range 0..ffff\n"); + return CLI_OK; + } + + clipkt->ip_sum = (u_int16_t) sum; + clipkt->ip_sum_false=1; + + return CLI_OK; +} + + + + + + + +int cmd_ip_tos (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + char *tmp; + + if (strncmp(argv[argc-1], "?", 2)==0) + { + cli_print(cli, "Specify the Type of Service field: <IPP> [<ToS>] [MBZ]\n"); + cli_print(cli, " - IP precedence (IPP) 0..7\r"); + cli_print(cli, " - ToS: delay/throughput/reliability/cost 0..15\r"); + cli_print(cli, " - MBZ ('must be zero' - however, not with Mausezahn...)\r"); + cli_print(cli, "Or, alternatively, configure the whole byte in hex.\n"); + cli_print(cli, "EXAMPLES:\n"); + cli_print(cli, " 5 ... IPP = 5\r"); + cli_print(cli, " 5 9 ... IPP = 5 and ToS = 9\r"); + cli_print(cli, " 5 MBZ ... IPP = 5 and MBZ is set\r"); + cli_print(cli, " 5 9 MBZ ... All three fields configured\r"); + cli_print(cli, " hex a8 ... the whole byte is set to 10101000\r"); + cli_print(cli, " 10101000 ... the whole byte in binary\n"); + return CLI_OK; + } + + if ((argc==1) && (mz_strisbinary(argv[0])==8)) + { + clipkt->ip_tos = (u_int8_t) str2bin8 (argv[0]); + return CLI_OK; + } + + if ((argc==2) && (mz_strcmp(argv[0], "hex", 2)==0)) + { + tmp = argv[1]; + + if (strlen(tmp)!=2) + { + cli_print(cli, "You must specify a 2-digit hexadecimal value\n"); + return CLI_OK; + } + + if (!(isxdigit(tmp[0])) || (!(isxdigit(tmp[1])))) + { + cli_print(cli, "Non-hexadecimal value!\n"); + return CLI_OK; + } + + clipkt->ip_tos = (u_int8_t) xstr2int (tmp); + return CLI_OK; + } + + switch (argc) + { + case 1: + if (mz_strcmp(argv[0], "mbz", 1)==0) + { + mops_ip_tos(clipkt, -1, -1, 1); + } + else + { + if (mops_ip_tos(clipkt, (int)str2int(argv[0]), -1, 0)) + cli_print(cli, "Invalid IP Precedence value\n"); + } + break; + + case 2: + if (mz_strcmp(argv[1], "mbz", 1)==0) + { + if (mops_ip_tos(clipkt, (int)str2int(argv[0]), -1, 1)) + cli_print(cli, "Invalid IP Precedence value\n"); + } + else + { + if (mops_ip_tos(clipkt, (int)str2int(argv[0]), (int)str2int(argv[1]), 0)) + cli_print(cli, "Invalid values\n"); + } + break; + + case 3: + if (mz_strcmp(argv[2], "mbz", 1)!=0) + cli_print(cli, "In this case the 3rd argument must be 'mbz'\n"); + else + if (mops_ip_tos(clipkt, (int)str2int(argv[0]), (int)str2int(argv[1]), 1)) + cli_print(cli, "Invalid values\n"); + break; + } + + return CLI_OK; +} + + + + + + + +int cmd_ip_dscp (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + if ((argc!=1) || (strncmp(argv[argc-1], "?", 2)==0)) + { + cli_print(cli, "Specify the Type of Service field using the DSCP format.\r"); + cli_print(cli, "Multiple notations are supported.\n"); + cli_print(cli, "Examples:\r"); + cli_print(cli, " AF32 .... specify AF codepoint with class 3 and drop probability 2\r"); + cli_print(cli, " EF .... specify Expedited Forwarding\r"); + cli_print(cli, " CS7 .... specify Code Selector 7\r"); + cli_print(cli, " 101110 .... specify the DSCP in binary\r"); + cli_print(cli, " 56 .... specify the DSCP in decimal\r"); + cli_print(cli, "\r"); + return CLI_OK; + } + + switch (mops_ip_dscp(clipkt, argv[0])) + { + case -1: + cli_print(cli, "Invalid DSCP specification (use '?')\n"); + break; + case 1: + cli_print(cli, "Invalid AF code point (use '?')\n"); + break; + case 2: + cli_print(cli, "Invalid Code Selector (CS0..CS7)\n"); + break; + case 3: + cli_print(cli, "Invalid DSCP value (0..63)\n"); + break; + } + + return CLI_OK; +} + + + + + +int cmd_ip_rsv (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + + if (strncmp(argv[argc-1], "?", 2)==0) + { + cli_print(cli, "Set or unset the reserved flag.\n"); + return CLI_OK; + } + + if (argc!=1) + { + cli_print(cli, "Use the 'set' or 'unset' keywords.\n"); + return CLI_OK; + } + + + if (mz_strcmp(argv[0], "set", 1)==0) + { + clipkt->ip_flags_RS = 1; + return CLI_OK; + } + + if (mz_strcmp(argv[0], "unset", 1)==0) + { + clipkt->ip_flags_RS = 0; + return CLI_OK; + } + + cli_print(cli, "Unknown keyword. Use the 'set' or 'unset' keywords.\n"); + + return CLI_OK; + +} + + + + + +int cmd_ip_df (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + + if (strncmp(argv[argc-1], "?", 2)==0) + { + cli_print(cli, "Set or unset the don't fragment flag.\n"); + + return CLI_OK; + } + + if (argc!=1) + { + cli_print(cli, "Use the 'set' or 'unset' keywords.\n"); + return CLI_OK; + } + + + if (mz_strcmp(argv[0], "set", 1)==0) + { + clipkt->ip_flags_DF = 1; + return CLI_OK; + } + + if (mz_strcmp(argv[0], "unset", 1)==0) + { + clipkt->ip_flags_DF = 0; + return CLI_OK; + } + + cli_print(cli, "Unknown keyword. Use the 'set' or 'unset' keywords.\n"); + + + return CLI_OK; + +} + + + + + +int cmd_ip_mf (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + + if (strncmp(argv[argc-1], "?", 2)==0) + { + cli_print(cli, "Set or unset the more fragments flag.\n"); + + return CLI_OK; + } + + if (argc!=1) + { + cli_print(cli, "Use the 'set' or 'unset' keywords.\n"); + return CLI_OK; + } + + + if (mz_strcmp(argv[0], "set", 1)==0) + { + clipkt->ip_flags_MF = 1; + return CLI_OK; + } + + if (mz_strcmp(argv[0], "unset", 1)==0) + { + clipkt->ip_flags_MF = 0; + return CLI_OK; + } + + cli_print(cli, "Unknown keyword. Use the 'set' or 'unset' keywords.\n"); + + return CLI_OK; + +} + + +int cmd_ip_fragsize (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + u_int32_t fragsize=0; + + if (strncmp(argv[argc-1], "?", 2)==0) { + cli_print(cli, "Enable fragmentation by configuring a fragment size.\n"); + cli_print(cli, "Note that the fragment size specifies the number of bytes in the IP payload\r"); + cli_print(cli, "and NOT the assumed MTU on that link. The total packet size of each fragment\r"); + cli_print(cli, "will be 20 bytes larger (=size of IP header if no IP options are used).\n"); + cli_print(cli, "WARNING: The fragment size SHOULD be a multiple of 8 bytes if you expect\r"); + cli_print(cli, " a valid result.\n"); + cli_print(cli, "ARGUMENTS: <frag-size>\n"); + return CLI_OK; + } + + if (argc!=1) { + cli_print(cli, "Specify the fragment size in bytes.\n"); + return CLI_OK; + } + + + fragsize = (u_int32_t) str2int(argv[0]); + + if ((fragsize<0) || (fragsize>8000)) { + cli_print(cli, "The fragment size must be within range 0..8000\n"); + return CLI_OK; + } + + if (fragsize%8) { + cli_print(cli, "Warning: The fragment-size is not a multiple of 8.\n"); + } + + clipkt->ip_fragsize = fragsize; + + return CLI_OK; + +} + + + +int cmd_ip_fragoverlap (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + u_int32_t overlap=0; + + if (strncmp(argv[argc-1], "?", 2)==0) { + cli_print(cli, "Specify how many bytes should overlap when IP fragmentation is enabled.\n"); + cli_print(cli, "NOTE: The number of overlap bytes is either 0 (default, no overlap) or\r"); + cli_print(cli, " a multiple of 8 bytes but smaller than frag-size.\n"); + cli_print(cli, "ARGUMENTS: <overlap>\n"); + return CLI_OK; + } + + if (argc!=1) { + cli_print(cli, "Specify how many bytes should overlap between successive IP fragments.\n"); + return CLI_OK; + } + + + overlap = (u_int32_t) str2int(argv[0]); + + if (clipkt->ip_fragsize == 0) { + cli_print(cli, "Please configure the fragment size first.\n"); + return CLI_OK; + } + + if ((overlap>clipkt->ip_fragsize) || (overlap%8)) { + cli_print(cli, "The overlap MUST be a multiple of 8 and MUST NOT exceed frag-size!\n"); + return CLI_OK; + } + + clipkt->ip_frag_overlap = overlap; + + return CLI_OK; +} + + + + + +int cmd_ip_option (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + int val=0; + + if ((strncmp(argv[argc-1], "?", 2)==0) || (argc==0)) { + cli_print(cli, "Add or delete IP options.\n"); + cli_print(cli, "You can only add one option after the other; if you want to configure multiple\r"); + cli_print(cli, "options then repeat this command. The options are added to the IP header in the\r"); + cli_print(cli, "same order as you configure them.\n"); + cli_print(cli, "Currently the following options are supported:\n"); + cli_print(cli, "router-alert [<value>] ... signal transit routers to examine the content of this\r"); + cli_print(cli, " packet.\r"); + cli_print(cli, "\n"); + cli_print(cli, "clear ..................... remove all options from the packet\n"); + return CLI_OK; + } + + if (mz_strcmp(argv[0], "router-alert", 3)==0) { + switch (argc) { + case 1: + val=0; + break; + case 2: + val = (int) str2int(argv[1]); + break; + default: + cli_print(cli, "Too many arguments!\n"); + return CLI_OK; + } + if (mops_ip_option_ra (clipkt, val)) { + cli_print(cli, "Value must be within 0..65535\n"); + return CLI_OK; + } + + } else if (mz_strcmp(argv[0], "loose-source-route", 3)==0) { + cli_print(cli, "Currently not implemented\n"); + return CLI_OK; + } else if (mz_strcmp(argv[0], "record-route", 3)==0) { + cli_print(cli, "Currently not implemented\n"); + return CLI_OK; + } + + else if (mz_strcmp(argv[0], "clear", 2)==0) { + mops_ip_option_remove_all (clipkt); + } + + return CLI_OK; +} + + + +// By default we use ARP to determine the destination MAC and therefore support +// automatic (in)direct delivery of IP packets. Alternatively the user may turn +// this off and may configure an arbitrary destination MAC address +// +int cmd_ip_delivery (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + char str[16]; + if (strncmp(argv[argc-1], "?", 2)==0) { + cli_print(cli, "Enable or disable IP auto-delivery.\n"); + sprintf(str, "%s", (clipkt->auto_delivery_off) ? "DISABLED" : "ENABLED"); + cli_print(cli, "Currently, IP auto-delivery is %s\n", str); + return CLI_OK; + } + + if (argc!=1) { + cli_print(cli, "Argument missing. Enter either 'enable' or 'disable'\n"); + return CLI_OK; + } + + if (mz_strcmp(argv[0], "enable", 1)==0) + clipkt->auto_delivery_off=0; + else if (mz_strcmp(argv[0], "disable", 1)==0) + clipkt->auto_delivery_off=1; + else { + cli_print(cli, "Unknown keyword. Enter either 'enable' or 'disable'\n"); + return CLI_OK; + } + + sprintf(str, "%s", (clipkt->auto_delivery_off) ? "DISABLED" : "ENABLED"); + cli_print(cli, "IP auto-delivery is now %s\n", str); + + return CLI_OK; + +} + + + +int cmd_ip_end(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + char prompt[16]; + sprintf(prompt, "pkt-%i",clipkt->id); + cli_set_configmode(cli, MZ_MODE_PACKET, prompt); + return CLI_OK; +} diff --git a/staging/cli_launch.c b/staging/cli_launch.c new file mode 100644 index 0000000..19b6ba0 --- /dev/null +++ b/staging/cli_launch.c @@ -0,0 +1,141 @@ +/* + * 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 + * +*/ + + + +#include "mz.h" +#include "cli.h" +#include "mops.h" + + + +int launch_bpdu (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + int conf=0; + struct mops_ext_bpdu * pd; + + if ( (strncmp(argv[argc-1],"?",2)==0) || (argc>1) ) { + cli_print(cli, "Launch a(nother) BPDU process:\n"); + cli_print(cli, "<CR> Per default a TCN-BPDU is sent.\r"); + cli_print(cli, "conf Use this keyword to emit configuration BPDUs\r"); + cli_print(cli, " (with this host as root bridge)\n"); + return CLI_OK; + } + + if (argc==1) { + if (mz_strcmp(argv[0], "conf", 1)==0) conf=1; + } + + if ((clipkt = mops_alloc_packet(mp_head)) == NULL) { // Problem, memory full? + cli_print(cli, "Cannot allocate additional memory!\n"); + return CLI_OK; + } + + strncpy (clipkt->packet_name, "sysBPDU", 7); + // OK, created a new packet + cli_print(cli, "Allocated new packet %s at slot %i",clipkt->packet_name, clipkt->id); + mops_set_defaults(clipkt); + if (mops_ext_add_pdesc (clipkt, MOPS_BPDU)) + cli_print(cli, "Cannot configure BPDU parameters!?\n"); + else { + clipkt->use_ETHER = 1; + clipkt->use_SNAP = 1; + clipkt->count = 0; + clipkt->ndelay.tv_sec = 2; + clipkt->ndelay.tv_nsec = 0; + pd = clipkt->p_desc; + if (conf) + pd->bpdu_type = 0x00; + else + pd->bpdu_type = 0x80; + mops_set_conf(clipkt); + if (mops_tx_simple (clipkt)) { + cli_print(cli, "Cannot create sending process.\r"); + } + } + + return CLI_OK; +} + + + +int launch_synflood (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + u_int8_t IP[4]; + int valid_ip=0, valid_port=0; + + if ( (strncmp(argv[argc-1],"?",2)==0) || (argc>2) || (argc==0)) { + cli_print(cli, "Launch a(nother) TCP SYN-Flood process:\n"); + cli_print(cli, "<dst-ip-addr> At least you must specify the destination IP address\r"); + cli_print(cli, "<dst-ip-addr> <port-nr> Optionally specify the destination port (default: range from 1-1023)\n"); + return CLI_OK; + } + + if (mops_pdesc_ip (IP, argv[0])==0) { // check if format is really an IP address + valid_ip=1; + } else { + cli_print(cli, "Invalid IP address\n"); + return CLI_OK; + } + + if (argc==2) { + if (mz_strisnum(argv[1])==0) { + cli_print(cli, "Invalid port number\n"); + return CLI_OK; + } + valid_port = (int) str2int(argv[1]); + if (valid_port>65535) { + cli_print(cli, "Invalid port number\n"); + return CLI_OK; + } + } + + + if ((clipkt = mops_alloc_packet(mp_head)) == NULL) { // Problem, memory full? + cli_print(cli, "Cannot allocate additional memory!\n"); + return CLI_OK; + } + + strncpy (clipkt->packet_name, "sysFlood_TCPSYN", 15); + // OK, created a new packet + cli_print(cli, "Allocated new packet %s at slot %i",clipkt->packet_name, clipkt->id); + mops_set_defaults(clipkt); + clipkt->use_ETHER = 1; + clipkt->use_IP = 1; + clipkt->use_TCP = 1; + clipkt->ip_proto = 6; + clipkt->count = 0; + clipkt->ip_dst = str2ip32(argv[0]); + clipkt->ip_src_israndom=1; + if (valid_port) { + clipkt->dp = valid_port; + } else { + clipkt->dp_isrange=1; + clipkt->dp_start=1; + clipkt->dp_stop=1023; + } + clipkt->ndelay.tv_sec = 0; + clipkt->ndelay.tv_nsec = 0; + mops_set_conf(clipkt); + mops_tcp_add_option (clipkt,64,0,0,0,0); + if (mops_tx_simple (clipkt)) { + cli_print(cli, "Cannot create sending process.\r"); + } + + return CLI_OK; +} diff --git a/staging/cli_legacy.c b/staging/cli_legacy.c new file mode 100644 index 0000000..2adc535 --- /dev/null +++ b/staging/cli_legacy.c @@ -0,0 +1,141 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008 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 + * +*/ + + +#include "mz.h" +#include "cli.h" +#include "mops.h" + + +int transmit (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + int i; + char argstr[10000]; + + argstr[0]='\0'; + + if (argc>1) + { + for (i=1; i<argc; i++) + { + if ((strlen(argv[i])+strlen(argstr))>10000) + { + cli_print(cli, "Argument list too long!\n"); + return CLI_OK; + } + if (strncmp(argv[i], "?", 1)==0) + { + strcat(argstr, ",help"); + } + else + strncat(argstr, argv[i], 5000); // TODO: This is ugly! + } + // TEST: cli_print(cli, "argc=%i, got '%s'\n", argc, argstr); + } + + + if (argv[0] == NULL) // raw hex string given + { + mode = BYTE_STREAM; + } + else if (strcmp(argv[0],"arp")==0) + { + mode = ARP; + } + else if (strcmp(argv[0],"bpdu")==0) + { + mode = BPDU; + } + else if (strcmp(argv[0],"ip")==0) + { + mode = IP; + } + else if (strcmp(argv[0],"udp")==0) + { + mode = UDP; + } + else if (strcmp(argv[0],"icmp")==0) + { + mode = ICMP; + } + else if (strcmp(argv[0],"tcp")==0) + { + mode = TCP; + } + else if (strcmp(argv[0],"dns")==0) + { + mode = DNS; + } + else if (strcmp(argv[0],"cdp")==0) + { + mode = CDP; + } + else if (strcmp(argv[0],"syslog")==0) + { + mode = SYSLOG; + } + else if (strcmp(argv[0],"lldp")==0) + { + mode = LLDP; + tx.packet_mode=0; // create whole frame by ourself + } + else if (strcmp(argv[0],"rtp")==0) + { + mode = RTP; + } + else if (strcmp(argv[0],"raw")==0) + { + strncpy(tx.arg_string, argstr, MAX_PAYLOAD_SIZE); + send_eth(); + } + else if (strcmp(argv[0],"?")==0) + { + cli_print(cli, + "| The following packet types are currently implemented:\n" + "|\n" + "| arp ... sends ARP packets\n" + "| bpdu ... sends BPDU packets (STP)\n" + "| cdp ... sends CDP messages\n" + "| ip ... sends IPv4 packets\n" + "| udp ... sends UDP datagrams\n" + "| tcp ... sends TCP segments\n" + "| icmp ... sends ICMP messages\n" + "| dns ... sends DNS messages\n" + "| rtp ... sends RTP datagrams\n" + "| syslog ... sends Syslog messages\n" + "| lldp ... sends LLDP datagrams\n" + "|\n" + "| raw ... raw layer 2 mode (specify whole frame in hex)\n" + "\n" + ); + return CLI_OK; + } + else + { + cli_print(cli, "Unknown packet type '%s'\r", argv[0]); + } + + + if (mode) + { + strncpy(tx.arg_string, argstr, MAX_PAYLOAD_SIZE); + tx_switch(cli); + } + + return CLI_OK; +} diff --git a/staging/cli_lldp.c b/staging/cli_lldp.c new file mode 100644 index 0000000..8991137 --- /dev/null +++ b/staging/cli_lldp.c @@ -0,0 +1,437 @@ +/* + * 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 + * +*/ + + + +#include "mz.h" +#include "cli.h" +#include "mops.h" + + +int cmd_lldp_conformance (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + struct mops_ext_lldp * pd = clipkt->p_desc; + + + if ( (strcmp(argv[argc-1],"?")==0) || (argc!=1)) { + cli_print(cli, "Enables or disables LLDP standard conformance mode.\n"); + cli_print(cli, "Keywords: enable | disable\n"); + cli_print(cli, "Per default, standard LLDP messages are created which require a fixed\r"); + cli_print(cli, "order of the mandatory TLVs. If the standard conformance mode is disabled\r"); + cli_print(cli, "then you can configure an arbitrary sequence of LLDP TLVs. \n"); + cli_print(cli, "Currently, the LLDP standard conformance mode is %s\n", (pd->non_conform) ? "DISABLED" : "ENABLED"); + return CLI_OK; + } + + if (mz_strcmp(argv[0], "enable", 1)==0) { + pd->non_conform = 0; + return CLI_OK; + } + + + if (mz_strcmp(argv[0], "disable", 1)==0) { + pd->non_conform = 1; + return CLI_OK; + } + + cli_print(cli, "Enter enable or disable\n"); + return CLI_OK; +} + + +int cmd_lldp_chassis_id (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + struct mops_ext_lldp * pd = clipkt->p_desc; + int subtype = 4; + char *cid; + int cl, cidl; + u_int8_t tmp[512]; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>2)) { + cli_print(cli, "Configure a Chassis ID TLV.\n"); + cli_print(cli, "ARGUMENTS: [<subtype>] <chassis-id>\n"); + cli_print(cli, "By default the <subtype> is of kind 'mac address (4)' and the <chassis-id>\r"); + cli_print(cli, "must be a hexadecimal string (e. g. 00:01:ca:fe:de:ad) of max 255 bytes\n"); + return CLI_OK; + } + + if (argc==2) { + subtype = (int) str2int(argv[0]); + if ((subtype>255) || (mz_strisnum(argv[0])==0)) { + cli_print(cli, "Invalid subtype\n"); + return CLI_OK; + } + cid = argv[1]; + } else + cid = argv[0]; + + cl=strnlen(cid, 1024); + + if (cl>=1024) { + cli_print(cli, "Chassis-ID too long\n"); + return CLI_OK; + } else cidl=str2hex(cid, tmp, 511); + + if (pd->non_conform == 0) { + pd->chassis_id_subtype = subtype; + memcpy((void*) pd->chassis_id, (void*)tmp, cidl); + pd->chassis_id_len = cidl; + } else { + // non_conform + mops_lldp_opt_tlv_chassis (clipkt, subtype, cidl, tmp); + } + + return CLI_OK; +} + + + + +int cmd_lldp_port_id (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + struct mops_ext_lldp * pd = clipkt->p_desc; + int subtype = 4; + char *pid; + int pl; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>2)) { + cli_print(cli, "Configure a Port ID TLV.\n"); + cli_print(cli, "ARGUMENTS: [<subtype>] <port-id>\n"); + cli_print(cli, "By default the <subtype> is of kind 'Interface name (5)' and the <port-id>\r"); + cli_print(cli, "must be a ascii string (usually the name of the interface e. g. eth3) of\r"); + cli_print(cli, "max 255 bytes.\n"); + return CLI_OK; + } + + if (argc==2) { + subtype = (int) str2int(argv[0]); + if ((subtype>255) || (mz_strisnum(argv[0])==0)) { + cli_print(cli, "Invalid subtype\n"); + return CLI_OK; + } + pid = argv[1]; + } else + pid = argv[0]; + + pl=strnlen(pid, 256); + + if (pl>255) { + cli_print(cli, "Port-ID too long\n"); + return CLI_OK; + } + + + if (pd->non_conform == 0) { + pd->port_id_subtype = subtype; + memcpy((void*) pd->port_id, (void*) pid, pl); + pd->port_id_len = pl; + } else { + // non_conform + mops_lldp_opt_tlv_port (clipkt, subtype, pl, (u_int8_t*) pid); + } + + return CLI_OK; +} + + + +int cmd_lldp_ttl (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + struct mops_ext_lldp * pd = clipkt->p_desc; + int ttl; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc!=1)) { + cli_print(cli, "Configure the LLDP TTL.\n"); + cli_print(cli, "ARGUMENTS: <time-to-live>\n"); + cli_print(cli, "The TTL must be within 0..65535\n"); + return CLI_OK; + } + + ttl = (int) str2int(argv[0]); + + if (mz_strisnum(argv[0])==0) { + cli_print(cli, "Invalid argument\n"); + return CLI_OK; + } + + if (ttl>0xffff) { + cli_print(cli, "TTL must be within 0..65535\n"); + return CLI_OK; + } + + if (pd->non_conform == 0) { + pd->TTL = ttl; + } else { + // non_conform + mops_lldp_opt_tlv_TTL (clipkt, ttl); + } + + return CLI_OK; +} + + + +int cmd_lldp_vlan (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + int vlan; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc!=1)) { + cli_print(cli, "Configure the LLDP Port VLAN-ID.\n"); + cli_print(cli, "ARGUMENTS: <vlan-id>\n"); + cli_print(cli, "The vlan-id must be within 0..65535\n"); + return CLI_OK; + } + + vlan = (int) str2int(argv[0]); + + if (mz_strisnum(argv[0])==0) { + cli_print(cli, "Invalid argument\n"); + return CLI_OK; + } + + if (vlan>0xffff) { + cli_print(cli, "The VLAN-ID must be within 0..65535\n"); + return CLI_OK; + } + + + mops_lldp_opt_tlv_vlan (clipkt, vlan); + + return CLI_OK; +} + + + +int cmd_lldp_opt_tlv (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + int type=0, len=0; + u_int8_t tmp[512]; + + + if ( (strcmp(argv[argc-1],"?")==0) || (argc!=3)) { + cli_print(cli, "Configure an arbitrary optional TLV.\n"); + cli_print(cli, "ARGUMENTS: ascii|hex <type> <value>\n"); + cli_print(cli, "The TLV type must be between 0..127, the value length is up to 511 bytes.\n"); + return CLI_OK; + } + + + if (mz_strcmp(argv[0], "ascii", 1)==0) { + if ((len=strnlen(argv[2],512))>511) { + cli_print(cli, "<value> must be smaller or equal 511 characters\n"); + return CLI_OK; + } + mz_strncpy((char*) tmp, argv[2], 511); + } else if (mz_strcmp(argv[0], "hex", 1)==0) { + len=str2hex(argv[2], tmp, 512); + if (len>511) { + cli_print(cli, "<value> must be smaller or equal 511 bytes\n"); + return CLI_OK; + } + } + + type = (int) str2int(argv[1]); + + if (mz_strisnum(argv[1])==0) { + cli_print(cli, "Invalid type\n"); + return CLI_OK; + } + + if (type>127) { + cli_print(cli, "<type> must be within 0..127\n"); + return CLI_OK; + } + + + if (mops_lldp_opt_tlv (clipkt, type, len, tmp)==0) + cli_print(cli, "Invalid TLV values\n"); + + return CLI_OK; +} + + + + +int cmd_lldp_opt_tlv_bad (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + int type, len=0, wronglen; + u_int8_t tmp[512]; + + + if ( (strcmp(argv[argc-1],"?")==0) || (argc!=4)) { + cli_print(cli, "Configure an arbitrary optional *BAD* TLV.\n"); + cli_print(cli, "ARGUMENTS: ascii|hex <type> <wrong-length> <value>\n"); + cli_print(cli, "Using this command you can add a custom TLV with a wrong length parameter.\r"); + cli_print(cli, "Such TLV can be used to verify whether LLDP receivers are robust enough\r\r"); + cli_print(cli, "since a too small <wrong-length> could cause a buffer overflow. The TLV type\r"); + cli_print(cli, "must be between 0..127, the <wrong-length> can be within 0..511 (and can be\r"); + cli_print(cli, "also the true length of course\n"); + return CLI_OK; + } + + + if (mz_strcmp(argv[0], "ascii", 1)==0) { + if ((len=strnlen(argv[3],512))>511) { + cli_print(cli, "<value> must be smaller or equal 511 characters\n"); + return CLI_OK; + } + mz_strncpy((char*) tmp, argv[3], 511); + } else if (mz_strcmp(argv[0], "hex", 1)==0) { + len=str2hex(argv[3], tmp, 512); + if (len>511) { + cli_print(cli, "<value> must be smaller or equal 511 bytes\n"); + return CLI_OK; + } + } + + type = (int) str2int(argv[1]); + + if (mz_strisnum(argv[1])==0) { + cli_print(cli, "Invalid type\n"); + return CLI_OK; + } + + if (type>127) { + cli_print(cli, "<type> must be within 0..127\n"); + return CLI_OK; + } + + wronglen = (int) str2int(argv[2]); + + if (mz_strisnum(argv[2])==0) { + cli_print(cli, "Invalid length\n"); + return CLI_OK; + } + + if (wronglen>511) { + cli_print(cli, "<wrong-length> must be within 0..511\n"); + return CLI_OK; + } + + if (mops_lldp_opt_tlv_bad (clipkt, type, wronglen, len, tmp)==0) + cli_print(cli, "Invalid TLV values\n"); + + return CLI_OK; +} + + + +int cmd_lldp_opt_org (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + int subtype, len=0, oui=0; + u_int8_t tmp[512]; + + + if ( (strcmp(argv[argc-1],"?")==0) || (argc!=4)) { + cli_print(cli, "Configure an organisational TLV.\n"); + cli_print(cli, "ARGUMENTS: ascii|hex <oui> <subtype> <value>\n"); + cli_print(cli, "Using this command you can add an arbitrary organisational TLV. The <oui> represents\r"); + cli_print(cli, "the 'Organisational Unique Identifier' and consists of exactly three bytes in hexadecimal\r"); + cli_print(cli, "format, such as '00005e' The <subtype> is a value between <0..255>, and the length of the\r"); + cli_print(cli, "value is up to 507 bytes.\n"); + return CLI_OK; + } + + + if (mz_strcmp(argv[0], "ascii", 1)==0) { + if ((len=strnlen(argv[3],512))>511) { + cli_print(cli, "<value> must be smaller or equal 511 characters\n"); + return CLI_OK; + } + mz_strncpy((char*) tmp, argv[3], 511); + } else if (mz_strcmp(argv[0], "hex", 1)==0) { + len=str2hex(argv[3], tmp, 512); + if (len>511) { + cli_print(cli, "<value> must be smaller or equal 511 bytes\n"); + return CLI_OK; + } + } + + oui = xstr2int(argv[1]); + if (mz_strishex(argv[1])==0) { + cli_print(cli, "Invalid oui value\n"); + return CLI_OK; + } + + if (oui>0xffffff) { + cli_print(cli, "<oui> must be within 0..ffffff\n"); + return CLI_OK; + } + + subtype = (int) str2int(argv[2]); + + if (mz_strisnum(argv[2])==0) { + cli_print(cli, "Invalid subtype\n"); + return CLI_OK; + } + + if (subtype>255) { + cli_print(cli, "<subtype> must be within 0..255\n"); + return CLI_OK; + } + + + if (mops_lldp_opt_tlv_org (clipkt, oui, subtype, len, tmp)==0) + cli_print(cli, "Invalid TLV values\n"); + + return CLI_OK; +} + + + + + +int cmd_lldp_endtlv (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>0)) { + cli_print(cli, "Add an 'End of LLDP' TLV\n"); + cli_print(cli, "ARGUMENTS: none\n"); + cli_print(cli, "This command allows you to insert an 'End of LLDP' TLV at any\r"); + cli_print(cli, "point within the optional TLV list. You usually want this to\r"); + cli_print(cli, "create an invalid LLDPU to test the receiver.\n"); + return CLI_OK; + } + + + mops_lldp_opt_tlv_end (clipkt); + + return CLI_OK; +} + + +int cmd_lldp_reset (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>0)) { + cli_print(cli, "Reset the LLPDU and clear all optional TLVs.\n"); + cli_print(cli, "ARGUMENTS: none\n"); + cli_print(cli, "All optional TLVs are added in the sequence as you configure them.\r"); + cli_print(cli, "Use this command to reset the LLDP and reconfigure all optional\r"); + cli_print(cli, "TLVs again. Additionally the parameters of the mandatory TLVs are\r"); + cli_print(cli, "reset to defaults.\n"); + return CLI_OK; + } + + mops_init_pdesc_lldp(clipkt); + + return CLI_OK; +} + + + diff --git a/staging/cli_packet.c b/staging/cli_packet.c new file mode 100644 index 0000000..03fbd03 --- /dev/null +++ b/staging/cli_packet.c @@ -0,0 +1,1121 @@ +/* + * 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 + * +*/ + + +#include "mz.h" +#include "cli.h" +#include "mops.h" + + +int debug_packet (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + cli_debug = CLI_DEBUG_PACKET; + cli_print (cli, "Packet debugging enabled\n"); + return CLI_OK; +} + + + + +// Enter packet config mode: +// +// 1) either with an optional packet slot number => modify existing slot +// 2) or without number to allocate a new slot entry +// +int enter_packet (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + char prompt[16]; + + if (argc==0) { // Allocate new packet + if ((clipkt = mops_alloc_packet(mp_head)) == NULL) { // Problem, memory full? + cli_print(cli, "Holy flying spaghetti monster! Cannot allocate additional memory!\n"); + return CLI_OK; + } + // OK, created a new packet + snprintf(prompt, 16, "pkt-%i",clipkt->id); + cli_print(cli, "Allocated new packet %s at slot %i",clipkt->packet_name, clipkt->id); + // mops_set_defaults(clipkt); //// implicitly done by mops_alloc_packet + } else if ( (strcmp(argv[argc-1],"?")==0) || (argc>1) ) { + cli_print(cli, "<CR> create a new packet slot\r"); + cli_print(cli, "NAME enter packet slot of packet with name NAME\r"); + cli_print(cli, "ID enter packet slot of packet with number ID\n"); + return CLI_OK; + } else { // user specified a unique packet_name + if ( (clipkt = mops_search_name (mp_head, argv[0]))==NULL) { // packet name does not exist + if ( (clipkt = mops_search_id (mp_head, (int) str2int(argv[0])))==NULL) { // packet id does not exist + cli_print(cli, "Packet does not exist\n"); + return CLI_OK; + } + } + if (mops_is_any_active(clipkt)) { // don't allow to configure packets which are active! + cli_print(cli, "The selected packet is currently in active state!\r"); + cli_print(cli, "In order to configure this packet, please stop the associated packet process first.\n"); + return CLI_OK; + } + snprintf(prompt, 16, "pkt-%i",clipkt->id); + cli_print(cli, "Modify packet parameters for packet %s [%i]",clipkt->packet_name, clipkt->id); + } + cli_set_configmode(cli, MZ_MODE_PACKET, prompt); + //cli_print(cli, "Packet configuration mode - called %s with %s\r\n", __FUNCTION__, command); + return CLI_OK; +} + + + + + + + +// Specify the type and enter the appropriate configuration mode +// NOTE that we also reset and create the p_desc here! +int cmd_packet_type(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + char prompt[16]; + int ret=0; + char wrn[] = "Error: Could not create mops extension handle\n"; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc!=1) ) + { + cli_print(cli, "Specify a packet type from the following list:\r\n"); + cli_print(cli, " arp\r"); + cli_print(cli, " bpdu\r"); +// cli_print(cli, " cdp (not supported in this version)\r"); +// cli_print(cli, " icmp (not supported in this version)\r"); + cli_print(cli, " igmp\r"); + cli_print(cli, " ip\r"); + cli_print(cli, " lldp\r"); + cli_print(cli, " rtp\r"); +// cli_print(cli, " syslog (not supported in this version)\r"); + cli_print(cli, " tcp\r"); + cli_print(cli, " udp\r"); + return CLI_OK; + } + + if (mz_strcmp(argv[0],"arp",3) == 0) + { + if (mops_ext_add_pdesc (clipkt, MOPS_ARP)) + cli_print(cli, "%s", wrn); + else + { + ret=mops_clear_layers(clipkt, + MOPS_SNAP|MOPS_MPLS|MOPS_IP|MOPS_UDP|MOPS_TCP); + clipkt->use_ETHER = 1; + clipkt->eth_type = 0x806; + sprintf(prompt, "pkt-%i-arp",clipkt->id); + cli_set_configmode(cli, MZ_MODE_PACKET_ARP, prompt); + mops_update_arp(clipkt); + mops_set_conf(clipkt); + } + + } + else if (mz_strcmp(argv[0],"dns",3) == 0) + { + if (mops_ext_add_pdesc (clipkt, MOPS_DNS)) + cli_print(cli, "%s", wrn); + else + { + ret=mops_clear_layers(clipkt, MOPS_SNAP|MOPS_TCP); + clipkt->use_ETHER = 1; + clipkt->use_IP= 1; + clipkt->use_UDP= 1; + sprintf(prompt, "pkt-%i-dns",clipkt->id); + cli_set_configmode(cli, MZ_MODE_PACKET_DNS, prompt); + mops_set_conf(clipkt); + } + } + else if (mz_strcmp(argv[0],"icmp",3) == 0) + { + if (mops_ext_add_pdesc (clipkt, MOPS_ICMP)) + cli_print(cli, "%s", wrn); + else + { + ret=mops_clear_layers(clipkt, MOPS_SNAP|MOPS_TCP|MOPS_UDP); + clipkt->use_ETHER = 1; + clipkt->use_IP= 1; + sprintf(prompt, "pkt-%i-icmp",clipkt->id); + cli_set_configmode(cli, MZ_MODE_PACKET_ICMP, prompt); + mops_set_conf(clipkt); + } + } + else if (mz_strcmp(argv[0],"igmp",3) == 0) + { + if (mops_ext_add_pdesc (clipkt, MOPS_IGMP)) + cli_print(cli, "%s", wrn); + else + { + ret=mops_clear_layers(clipkt, MOPS_SNAP|MOPS_TCP|MOPS_UDP); + clipkt->use_ETHER = 1; + clipkt->use_IP= 1; + clipkt->ip_proto = 2; + mops_ip_option_ra(clipkt, 0); // add router alert option to IP header + sprintf(prompt, "pkt-%i-igmp",clipkt->id); + cli_set_configmode(cli, MZ_MODE_PACKET_IGMP, prompt); + mops_update_igmp(clipkt); + mops_set_conf(clipkt); + } + } + + else if (mz_strcmp(argv[0],"cdp",3) == 0) + { + if (mops_ext_add_pdesc (clipkt, MOPS_CDP)) + cli_print(cli, "%s", wrn); + else + { + ret=mops_clear_layers(clipkt, MOPS_ALL); + clipkt->use_ETHER = 1; + sprintf(prompt, "pkt-%i-cdp",clipkt->id); + cli_set_configmode(cli, MZ_MODE_PACKET_CDP, prompt); + mops_set_conf(clipkt); + } + } + else if (mz_strcmp(argv[0],"bpdu",3) == 0) + { + if (mops_ext_add_pdesc (clipkt, MOPS_BPDU)) + cli_print(cli, "%s", wrn); + else + { + ret=mops_clear_layers(clipkt, MOPS_MPLS|MOPS_IP|MOPS_UDP|MOPS_TCP); + clipkt->use_ETHER = 1; + clipkt->use_SNAP = 1; + sprintf(prompt, "pkt-%i-bpdu",clipkt->id); + cli_set_configmode(cli, MZ_MODE_PACKET_BPDU, prompt); + mops_update_bpdu(clipkt); + mops_set_conf(clipkt); + } + } + else if (mz_strcmp(argv[0],"ip",2) == 0) + { + ret=mops_clear_layers(clipkt, MOPS_TCP|MOPS_UDP); + clipkt->use_ETHER = 1; + clipkt->use_IP = 1; + sprintf(prompt, "pkt-%i-ip",clipkt->id); + cli_set_configmode(cli, MZ_MODE_PACKET_IP, prompt); + mops_set_conf(clipkt); + } + else if (mz_strcmp(argv[0],"udp",3) == 0) + { + ret=mops_clear_layers(clipkt, MOPS_SNAP|MOPS_TCP); + clipkt->use_ETHER = 1; + clipkt->use_IP = 1; + clipkt->use_UDP = 1; + clipkt->ip_proto = 17; + sprintf(prompt, "pkt-%i-udp",clipkt->id); + cli_set_configmode(cli, MZ_MODE_PACKET_UDP, prompt); + mops_set_conf(clipkt); + } + else if (mz_strcmp(argv[0],"tcp",3) == 0) + { + ret=mops_clear_layers(clipkt, MOPS_SNAP|MOPS_UDP); + clipkt->use_ETHER = 1; + clipkt->use_IP = 1; + clipkt->use_TCP = 1; + clipkt->ip_proto = 6; + sprintf(prompt, "pkt-%i-tcp",clipkt->id); + cli_set_configmode(cli, MZ_MODE_PACKET_TCP, prompt); + mops_set_conf(clipkt); + } + else if (mz_strcmp(argv[0],"syslog",3) == 0) + { + if (mops_ext_add_pdesc (clipkt, MOPS_SYSLOG)) + cli_print(cli, "%s", wrn); + else + { + ret=mops_clear_layers(clipkt, MOPS_SNAP|MOPS_TCP); + clipkt->use_ETHER = 1; + clipkt->use_IP = 1; + clipkt->use_UDP = 1; + sprintf(prompt, "pkt-%i-syslog",clipkt->id); + cli_set_configmode(cli, MZ_MODE_PACKET_SYSLOG, prompt); + mops_set_conf(clipkt); + } + } + else if (mz_strcmp(argv[0],"lldp",3) == 0) + { + if (mops_ext_add_pdesc (clipkt, MOPS_LLDP)) + cli_print(cli, "%s", wrn); + else + { + ret=mops_clear_layers(clipkt, MOPS_SNAP|MOPS_IP|MOPS_UDP|MOPS_TCP); + clipkt->use_ETHER = 1; + sprintf(prompt, "pkt-%i-lldp",clipkt->id); + cli_set_configmode(cli, MZ_MODE_PACKET_LLDP, prompt); + mops_set_conf(clipkt); + } + } + else if (mz_strcmp(argv[0],"rtp",3) == 0) + { + if (mops_ext_add_pdesc (clipkt, MOPS_RTP)) + cli_print(cli, "%s", wrn); + else + { + ret=mops_clear_layers(clipkt, MOPS_SNAP|MOPS_TCP); + clipkt->use_ETHER = 1; + clipkt->use_IP = 1; + clipkt->use_UDP = 1; + sprintf(prompt, "pkt-%i-rtp",clipkt->id); + cli_set_configmode(cli, MZ_MODE_PACKET_RTP, prompt); + mops_set_conf(clipkt); + } + } + + else // wrong user input + { + cli_print(cli, "Unknown type\n"); + return CLI_OK; + } + + if (ret) { + cli_print(cli, "Note that the following layer(2) have configured information:\r"); + if (ret & 1) cli_print(cli, " - Ethernet or 802.3\r"); + if (ret & 2) cli_print(cli, " - SNAP\r"); + if (ret & 4) cli_print(cli, " - 802.1Q\r"); + if (ret & 8) cli_print(cli, " - MPLS\r"); + if (ret & 16) cli_print(cli, " - IP\r"); + if (ret & 32) cli_print(cli, " - UDP\r"); + if (ret & 64) cli_print(cli, " - TCP\r"); + } + + mops_update(clipkt); + return CLI_OK; +} + + + + + +int cmd_packet_end(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + cli_set_configmode(cli, MODE_CONFIG, NULL); + return CLI_OK; +} + + + + + +int cmd_packet_clone (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + // TODO + return CLI_OK; +} + + +// Reserved words: "all", "slot" +int cmd_packet_name (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + if (strncmp(argv[0], "?", 2) == 0) + { + cli_print(cli, "Assign a packet name (max 16 chars)\n"); + return CLI_OK; + } + + if (argc>1) + { + cli_print(cli, "Packet name must not contain spaces\n"); + return CLI_OK; + } + + if (strlen(argv[0])>MAX_MOPS_PACKET_NAME_LEN) + { + cli_print(cli, "Packet name is limited to %i chars. You might use the 'description' command.\n",MAX_MOPS_PACKET_NAME_LEN); + return CLI_OK; + } + + if (mz_strcmp(argv[0], "all", 3)==0) + { + cli_print(cli, "This is a reserved word. Please choose another\n"); + return CLI_OK; + } + + strncpy(clipkt->packet_name, argv[0], MAX_MOPS_PACKET_NAME_LEN); + clipkt->packet_name[MAX_MOPS_PACKET_NAME_LEN-1] = 0x00; +// cli_print(cli, "Changed packet name to '%s'\n", clipkt->packet_name); + + return CLI_OK; +} + +int cmd_packet_description (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + if (strncmp(argv[argc-1], "?", 2) == 0) + { + cli_print(cli, "Assign a packet description (max %i chars)\n", MAX_MOPS_DESCRIPTION_LEN); + return CLI_OK; + } + + if (mops_pdesc_mstrings (clipkt->description, argv, argc, MAX_MOPS_DESCRIPTION_LEN)) + { + cli_print(cli, "String too long. Currently the description is limited to %i characters.\n", + MAX_MOPS_DESCRIPTION_LEN); + cli_print(cli, "Current description is:\n%s\n", clipkt->description); + } + + return CLI_OK; +} + +int cmd_packet_count (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + + if (strncmp(argv[argc-1], "?", 2) == 0) + { + cli_print(cli,"Specify the packet count. Zero means infinity.\n"); + return CLI_OK; + } + else if (argc) + { + clipkt->count = (unsigned long) str2int(argv[0]); + if (clipkt->count) + { + clipkt->cntx = clipkt->count; // count is finite: cntx will count down + } + else + { + clipkt->cntx = 0; // infinity: cntx will count up + } + return CLI_OK; + } + cli_print(cli,"Specify a packet count.\n"); + return CLI_OK; +} + + +int cmd_packet_delay (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + int ret=0; + char str[100]; + + if (strncmp(argv[argc-1], "?", 2) == 0) { + cli_print(cli, "delay <value> [hour | min | sec | msec | usec | nsec]\n"); + cli_print(cli, "Specify the inter-packet delay in hours, minutes, seconds, milliseconds, microseconds,\r"); + cli_print(cli, "or nanoseconds. The default unit is milliseconds (i. e. when no unit is given).\n"); + return CLI_OK; + } + + switch (argc) { + case 1: // only one argument, but may contain an unit (such as '314sec') + ret = delay_parse(&clipkt->ndelay, argv[0], NULL); + break; + + case 2: // user specified two arguments such as '100 msec' + ret = delay_parse(&clipkt->ndelay, argv[0], argv[1]); + break; + default: + cli_print(cli, "Too many arguments! Expected delay value and unit, such as '10 msec'\n"); + return CLI_OK; + } + + switch (ret) { + case 1: + cli_print(cli, "Invalid unit! Use one of {nsec, usec, msec, sec, min, hours}\n"); + return CLI_OK; + break; + case 2: + cli_print(cli, "Value too large! Supported range is from 0 to 999999999\n"); + return CLI_OK; + break; + } + sprintf(str, "Inter-packet delay set to %lu sec and %lu nsec", clipkt->ndelay.tv_sec, clipkt->ndelay.tv_nsec); + cli_print(cli, "%s\n", str); + + return CLI_OK; +} + + + +int cmd_packet_bind (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + int i; + + if (strncmp(argv[argc-1], "?", 2) == 0) + { + cli_print(cli,"<DEVICE> Change the packet's network interface\r"); + cli_print(cli,"default Use interface settings as packet default\n"); + return CLI_OK; + } + else if (argc) + { + if (mz_strcmp(argv[0], "default", 3)==0) + { + i = mops_get_device_index(clipkt->device); + // Copy device_list[i].ip_mops and .mac_mops to clipkt->ip_src and ->eth_src + memcpy((void *) &clipkt->eth_src, (void *) &device_list[i].mac_mops[0], 6); + memcpy((void *) &clipkt->ip_src, (void *) &device_list[i].ip_mops[0], 4); + } + else + { + i = mops_get_device_index(argv[0]); + + if (i != -1) + { + strncpy(clipkt->device, argv[0], 16); // assign device to this mops + mops_use_device(clipkt, i); + } + else + cli_print(cli, "Unknown device, will stick on %s\n", clipkt->device); + } + } + else + cli_print(cli, "Nothing specified, will stick on %s\n", clipkt->device); + + return CLI_OK; +} + + + + + + + +// FORMAT: <VLAN>:<CoS> such as: "100:3 17:5 ..." +// NOTE: LEFTMOST TAG = OUTER TAG IN FRAME +// CFI is set/unset separately (see ? below) +// Transmission format: 0x8100 plus CoS (3) CFI(1) VLAN(12) +int cmd_packet_dot1q (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + int i, j, k=0; + int n; + char Vlan[64], CoS[64]; + u_int16_t v,c; + + if (strcmp(argv[argc-1],"?")==0) + { + cli_print(cli, "Configure 802.1Q tags:\n"); + cli_print(cli, " VLAN[:CoS] [VLAN[:CoS]] ... The leftmost tag is the outer tag in the frame\r"); + cli_print(cli, " remove <tag-nr> | all Remove one or more tags (<tag-nr> starts with 1),\r"); + cli_print(cli, " by default the first (=leftmost,outer) tag is removed,\r"); + cli_print(cli, " keyword 'all' can be used instead of tag numbers.\r"); + cli_print(cli, " cfi | nocfi [<tag-nr>] Set or unset the CFI-bit in any tag (by default\r"); + cli_print(cli, " assuming the first tag).\n"); + return CLI_OK; + } + + if (argc==0) + { + cli_print(cli, "Specify one or more VLAN-IDs, optionally with CoS values\n"); + return CLI_OK; + } + + n = clipkt->dot1Q_s/4; // n = number of tags + +////////////////////////////////////////// + if (mz_strcmp(argv[0], "remove", 2)==0) + { + + if (argc>2) + { + cli_print(cli, "Too many arguments!\n"); + return CLI_OK; + } + + if (n==0) + { + cli_print(cli, "No 802.1Q tags present. None to be removed.\n"); + return CLI_OK; + } + + if ((argc==2) && (mz_strcmp(argv[1], "all", 1)==0)) + { + mops_dot1Q_remove(clipkt, 0); + return CLI_OK; + } + + if (argc==1) // no tag-nr specified => assume first tag + { + j=1; + } + else + { + j = (unsigned int) str2int(argv[1]); // take first argument + if (j==0) + { + cli_print(cli, "The tag-nr must be within {1..%i}\n", n); + return CLI_OK; + } + } + + // now remove tag + if (mops_dot1Q_remove(clipkt, j)) + { + cli_print(cli, "The packet only consists of %i tag(s)!\n", n); + } + return CLI_OK; + } + + +///////////////////////////////////////// + if (mz_strcmp(argv[0], "nocfi", 2)==0) + { + if (n==0) + { + cli_print(cli, "There are no 802.1Q tags yet!\n"); + return CLI_OK; + } + + if (argc>2) + { + cli_print(cli, "Invalid format!\n"); + return CLI_OK; + } + + if (argc==1) // no tag-nr specified => assume first tag + { + j=1; + } + else + { + j = (unsigned int) str2int(argv[1]); + } + + if (mops_dot1Q_nocfi(clipkt, j)) + { + cli_print(cli, "The packet only consists of %i tags!\n",k); + } + return CLI_OK; + } + +/////////////////////////////////////// + if (mz_strcmp(argv[0], "cfi", 2)==0) + { + if (n==0) + { + cli_print(cli, "There are no 802.1Q tags yet!\n"); + return CLI_OK; + } + + if (argc>2) + { + cli_print(cli, "Invalid format!\n"); + return CLI_OK; + } + + if (argc==1) // no tag-nr specified => assume first tag + { + j=1; + } + else + { + j = (unsigned int) str2int(argv[1]); + } + + if (mops_dot1Q_cfi(clipkt, j)) + { + cli_print(cli, "The packet only consists of %i tags!\n",k); + } + return CLI_OK; + } + + +///////////////////////// + for (i=0; i<argc; i++) // scan through all user tokens + { + v=0;c=0; k=0; + + if (mz_tok(argv[i],":",2, Vlan, CoS) == -1) + { + cli_print(cli, "Invalid format. Correct format: VLAN[:CoS] [VLAN[:CoS] ...]\n"); + return CLI_OK; + } + + if (Vlan[0]==0x00) + { + cli_print(cli, "[tag %i] Missing VLAN number\n", i+1); + return CLI_OK; + } + else + { + v = (u_int16_t) str2int(Vlan); + if (v>4095) + { + cli_print(cli, "[tag %i] VLAN number must not exceed 4095.\n", i+1); + return CLI_OK; + } + } + + if (CoS[0]==0x00) + { + c=0; + } + else + { + c = (u_int16_t) str2int(CoS); + if (c>7) + { + cli_print(cli, "[tag %i] CoS must not exceed 7.\n", i+1); + return CLI_OK; + } + } + + mops_dot1Q (clipkt, i, 1, v, c); // 3rd param '1' means 'new stack, also set dot1Q_s' + } + return CLI_OK; +} + + +// MPLS transmission format: Label(20) EXP(3) BoS(1) TTL(8) +// -- where BoS=0 indicate MORE labels, BoS=1 means last (bottom) label +// +// NOTE: The EtherType must be 0x8847 which identifies 'IP over MPLS' that is +// we do NOT need to set 0x800 for IP somewhere! Instead, mops_update() +// will always correctly set the single EtherType, if necessary after +// 802.1Q tags. For example when VLAN tags are present, the frame looks +// like this:----------------------------------vvvv----------------------- +// DMAC-SMAC-8100VLAN1-...-8100VLANn-EtherType(8847)-MPLS1-...-MPLSn-IP... +// +// MPLS Multicast packets are indicated by EtherType 8848 (!) +// See also RFC 5332 which allows both 'Codepoints' to carry MPLS multicast +// while 0x8848 only indicates multiaccess media. +// +// NOTE: If all MPLS labels are removed again, the original EtherType is restored again! +// The original EtherType is stored in mp->eth_type_backup +int cmd_packet_mpls (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + int a=0,i,j=0,k; + char LabelS[64], ExpS[64], TTLS[64]; + u_int32_t Label; + u_int8_t Exp; + u_int8_t TTL; + + + if ( (strcmp(argv[argc-1],"?")==0) || (argc==0) ) + { + cli_print(cli, "Configure one or more MPLS labels:\r"); + cli_print(cli, " LABEL[:EXP[:TTL]] [LABEL[:EXP[:TTL]]] ... The leftmost tag is the outer tag in frame\r"); + cli_print(cli, " remove <tag-nr> | all Remove tag with number <tag-nr> (starts with 1) or all.\r"); + cli_print(cli, " bos | nobos [<tag-nr>] Set/unset BoS flag, by default in last (rightmost) label\r"); + cli_print(cli, " unicast|multicast Choose EtherType 0x8847 or 0x8848 respectively\n"); + cli_print(cli, "Examples:\r"); + cli_print(cli, " tag mpls 100 200 300 Specify three tags, 100,200,300 \r"); + cli_print(cli, " tag mpls 100:5 200:5:1 Let first tag have CoS 5, second tag additionally uses TTL=1\r"); + cli_print(cli, " tag mpls 100::8 Let first tag have TTL=8\n"); + cli_print(cli, "Reserved label numbers:\r"); + cli_print(cli, " 0 ... explicit NULL (IPv4)\r"); + cli_print(cli, " 1 ... Router Alert\r"); + cli_print(cli, " 2 ... explicit NULL (IPv6)\r"); + cli_print(cli, " 3 ... implicit NULL (only announced within LDP)\r"); + cli_print(cli, " 14 ... OAM Alert (ITU-T Y.1711, RFC 3429)\n"); + return CLI_OK; + } + +/////////////////////////////////////////// + if (mz_strcmp(argv[0], "unicast", 2)==0) + { + if (clipkt->use_MPLS==0) + { + cli_print(cli, "First configure an MPLS label stack."); + return CLI_OK; + } + + if (argc>1) + { + cli_print(cli, "This command does not support any argument.\n"); + } + + clipkt->eth_type = 0x8847; + return CLI_OK; + } + +///////////////////////////////////////////// + if (mz_strcmp(argv[0], "multicast", 2)==0) + { + if (clipkt->use_MPLS==0) + { + cli_print(cli, "First configure an MPLS label stack."); + return CLI_OK; + } + + if (argc>1) + { + cli_print(cli, "This command does not support any argument.\n"); + } + + clipkt->eth_type = 0x8848; + return CLI_OK; + } + + k = clipkt->mpls_s/4; // number of available tags + +////////////////////////////////////////// + if (mz_strcmp(argv[0], "remove", 2)==0) + { + if (argc>2) + { + cli_print(cli, "Too many arguments!\n"); + return CLI_OK; + } + + if ((argc==2) && (mz_strcmp(argv[1], "all", 1)==0)) + { + if (mops_mpls_remove(clipkt, 0)) + cli_print(cli, "No MPLS label stack present. Nothing removed\n"); + return CLI_OK; + } + + if (argc==1) // no tag-nr specified => assume first tag + { + if (k==0) + cli_print(cli, "Currently the packet has no tag that can be removed.\n"); + else + j=1; + } + else + { + j = (unsigned int) str2int(argv[1]); // take first argument + } + if (mops_mpls_remove(clipkt, j)) + cli_print(cli, "The tag number must be within 1..%i\n",k); + + return CLI_OK; + } + +/////////////////////////////////////// + if (mz_strcmp(argv[0], "bos", 2)==0) + { + if (argc>2) + { + cli_print(cli, "Too many arguments\n"); + return CLI_OK; + } + if (argc==2) + { + i = (int) str2int(argv[1]); + if (i>k) + { + cli_print(cli, "Tag number exceeds actual number of tags (%i)\n",a); + return CLI_OK; + } + } + else // argc==1 (no tag number specified) + { + i = k; // default: last tag! + } + + mops_mpls_bos (clipkt, i); + return CLI_OK; + } + + if (mz_strcmp(argv[0], "nobos", 2)==0) + { + if (argc>2) + { + cli_print(cli, "Too many arguments\n"); + return CLI_OK; + } + if (argc==2) + { + i = (int) str2int(argv[1]); + if (i>k) + { + cli_print(cli, "Tag number exceeds actual number of tags (%i)\n",k); + return CLI_OK; + } + } + else // argc==1 (no tag number specified) + { + i = k; // default: last tag! + } + mops_mpls_nobos (clipkt, i); + return CLI_OK; + } + + +//////////////////////////////////////////// + for (i=0;i<argc;i++) // Get all labels + { + if (mz_tok(argv[i], ":", 3, LabelS, ExpS, TTLS) < 0) + { + cli_print(cli, "[Tag %i]Incorrect label specification! Use format LABEL[:EXP[:TTL]]\n", i+1); + return CLI_OK; + } + + // Get Label + if (LabelS[0]==0x00) + { + cli_print(cli, "[Tag %i] Invalid label value!\n", i+1); + return CLI_OK; + } + else + { + Label = (u_int32_t) str2int (LabelS); + if (Label > 1048575) + { + cli_print(cli, "[Tag %i] Label value cannot exceed 1048575\n", i+1); + return CLI_OK; + } + } + // Get EXP + if (ExpS[0]==0x00) + { + Exp=0; + } + else + { + Exp = (u_int8_t) str2int(ExpS); + if (Exp>7) + { + cli_print(cli, "[Tag %i] EXP value must be within range 0..7\n", i+1); + return CLI_OK; + } + } + + // Get TTL + if (TTLS[0]==0x00) + { + TTL=255; + } + else + { + if (str2int(TTLS)>255) + { + cli_print(cli, "[Tag%i] TTL value must be within range 0..255\n", i+1); + return CLI_OK; + } + TTL = (u_int8_t) str2int(TTLS); + } + + // Now add MPLS tag: + mops_mpls(clipkt, i, argc, Label, Exp, TTL); + } + + return CLI_OK; +} + + +// SYNTAX: +// +// payload hex ff:00:01:02:aa:bb:cc:dd aa:bb:cc +// payload hex file tmp/dump.dat +// +int cmd_packet_payload_hex (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + char str[MAX_MOPS_MSG_SIZE*3]; + int len, i; + + if (strncmp(argv[argc-1], "?", 2)==0) + { + cli_print(cli, "Specify a payload in hexadecimal format:\n"); + cli_print(cli, " XX:XX:XX:... Either directly as sequence of digits, separated by colon or space\r"); + cli_print(cli, " file <filename> Or specify a filename with hexadecimal digits as content\r"); + cli_print(cli, " (Also in the file the separator can be either a colon or a space)\n"); + cli_print(cli, "Example: \r"); + cli_print(cli, "payload hex ff:ff:ff:ff:ff:ff 00:12:34:56:67:89 08:00 ca:fe:ba:be\n"); + return CLI_OK; + } + + if (argc==0) + { + cli_print(cli, "Specify an ascii payload\n"); + return CLI_OK; + } + + + if (mz_strcmp(argv[0],"file", 2)==0) + { + // > > > > > ******* TODO: Open file and configure mops with filepointer ******** < < < < < < < < + cli_print(cli, "This feature is currently not supported.\n"); + return CLI_OK; + } + + // Get byte sequence - first copy into str + if (mops_pdesc_mstrings (str, argv, argc, MAX_MOPS_MSG_SIZE*3)) + { + cli_print(cli, "Payload too long (limited to %i bytes).\n", MAX_MOPS_MSG_SIZE); + } + else // str contains byte sequence now - convert into msg and set msg_s + { + len = strlen(str); + for (i=0; i<len; i++) + { + if (str[i]=='?') + { + cli_print(cli, "Specify hexadecimal digits {1234567890abcdef} or separators\n"); + return CLI_OK; + } + + if ( (!isxdigit(str[i])) && // Only allow "1234567890abcdefABCDEF", ":", ".", "-", and SPACE. + (!isspace(str[i])) && + (str[i]!=':') && + (str[i]!='.') && + (str[i]!='-')) + { + cli_print(cli, "Invalid character at position %i\n", i+1); + return CLI_OK; + } + } + + len = str2hex (str, clipkt->msg, MAX_MOPS_MSG_SIZE); + if (len==-1) + { + cli_print(cli, "Invalid byte sequence. Each byte must be specified with two hexadecimal digits.\n"); + return CLI_OK; + } + + clipkt->msg_s = (u_int32_t) len; + } + + return CLI_OK; +} + + + +int cmd_packet_payload_ascii (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + char str[MAX_MOPS_MSG_SIZE*3]; + int len, i; + + if (strncmp(argv[argc-1], "?", 2)==0) + { + cli_print(cli, "Specify a payload in ascii format.\r"); + cli_print(cli, "Note that multiple white spaces are replaced by a single white space. If you\r"); + cli_print(cli, "really want to specify multiple white spaces then use a dash '-' instead of\r"); + cli_print(cli, "a white space. If you want to specify a dash then use a caret '^' as escape\r"); + cli_print(cli, "character.\n"); + + return CLI_OK; + } + + if (argc==0) + { + cli_print(cli, "Specify an ascii payload\n"); + return CLI_OK; + } + + + if (mz_strcmp(argv[0],"file", 2)==0) + { + // > > > > > ******* TODO: Open file and configure mops with filepointer ******** < < < < < < < < + cli_print(cli, "This feature is currently not supported.\n"); + return CLI_OK; + } + + // Get byte sequence - first copy into str + if (mops_pdesc_mstrings (str, argv, argc, MAX_MOPS_MSG_SIZE)) + { + cli_print(cli, "Payload too long (limited to %i bytes).\n", MAX_MOPS_MSG_SIZE); + } + else // str contains byte sequence now - convert into msg and set msg_s + { + len = strlen(str); + for (i=0; i<len; i++) // Replace + { + if (str[i]=='-') + { + if ((i>0) && (str[i-1]=='^')) + { + memcpy((void*) &str[i-1], (void*) &str[i], len-i+1); + i--; len--; + } + else + { + str[i]=' '; + } + } + } + len--; // to eliminate the trailing space (created by mops_pdesc_mstring) + memcpy((void*) clipkt->msg, (void*) str, len); + clipkt->msg_s = len; + } + return CLI_OK; +} + + +int cmd_packet_payload_raw (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + + return CLI_OK; +} + + + +int cmd_packet_interval (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + unsigned long long iv, tv_sec=0; + + if (strncmp(argv[argc-1], "?", 1)==0) { + cli_print(cli, "Configure a greater packet interval in days, hours, minutes, or seconds\n"); + cli_print(cli, "Arguments: <value> <days | hours | minutes | seconds>\n"); + cli_print(cli, "Use a zero value to disable an interval.\n"); + return CLI_OK; + } + + if (argc!=2) { + cli_print(cli,"Enter a value and an unit\n"); + return CLI_OK; + } + + + if (mz_strisnum(argv[0])==0) { + cli_print(cli,"Invalid value\n"); + return CLI_OK; + } + + iv = str2lint(argv[0]); + + if (iv==0) { + cli_print(cli,"Interval disabled.\n"); + clipkt->interval_used = 0; + return CLI_OK; + } + + if (mz_strcmp(argv[1], "days", 1)==0) { + if (iv>365) { + cli_print(cli, "Supported range: 1..365 days\n"); + return CLI_OK; + } + tv_sec = 86400 * iv; + } + + if (mz_strcmp(argv[1], "hours", 1)==0) { + if (iv>1000) { + cli_print(cli, "Supported range: 1..1000 hours\n"); + return CLI_OK; + } + tv_sec = 3600 * iv; + } + + if (mz_strcmp(argv[1], "minutes", 1)==0) { + if (iv>1000) { + cli_print(cli, "Supported range: 1..1000 minutes\n"); + return CLI_OK; + } + tv_sec = 60 * iv; + } + + if (mz_strcmp(argv[1], "seconds", 1)==0) { + if (iv>999999) { + cli_print(cli, "Supported range: 1..999999 seconds\n"); + return CLI_OK; + } + tv_sec = iv; + } + + if (clipkt->count==0) { + cli_print(cli, "Note: reconfigured count value from 0 (infinity) to 1.\n"); + clipkt->count=1; + } + + if ((clipkt->count * clipkt->ndelay.tv_sec)>tv_sec) { + cli_print(cli, "Error: intervals are smaller than packet trains.\r"); + cli_print(cli, "Reduce either count or delay, or both\n"); + return CLI_OK; + } + + clipkt->interval.tv_sec = tv_sec; + clipkt->interval_used = 1; + + return CLI_OK; +} + diff --git a/staging/cli_rtp.c b/staging/cli_rtp.c new file mode 100644 index 0000000..28a6c2b --- /dev/null +++ b/staging/cli_rtp.c @@ -0,0 +1,343 @@ +/* + * 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 + * +*/ + + + +#include "mz.h" +#include "cli.h" +#include "mops.h" + + +int cmd_rtp_version (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + struct mops_ext_rtp * pd = clipkt->p_desc; + int v=2; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc!=1)) { + cli_print(cli, "Set the RTP version (0..3, default: v2).\n"); + return CLI_OK; + } + if (mz_strisnum(argv[0])==0) { + cli_print(cli, "Invalid number.\n"); + return CLI_OK; + } + v = (int) str2int(argv[0]); + if (v>3) { + cli_print(cli, "Range exceeded (0..3).\n"); + return CLI_OK; + } + pd->v = v; + return CLI_OK; +} + +int cmd_rtp_padding (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + struct mops_ext_rtp * pd = clipkt->p_desc; + char state[8]; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc!=1)) { + cli_print(cli, "Sets or unsets the RTP padding flag (default: disabled).\n"); + cli_print(cli, "Use the keywords 'set' or 'unset'.\n"); + sprintf(state, "%s", (pd->p) ? "SET" : "UNSET"); + cli_print(cli, "Current state of the padding flag: %s\n",state); + return CLI_OK; + } + + if (mz_strcmp(argv[0], "set", 1)==0) { + pd->p = 1; + } else if (mz_strcmp(argv[0], "unset", 1)==0) { + pd->p = 0; + } + else { + cli_print(cli, "Invalid keyword. Use 'set' or 'unset'.\n"); + } + + return CLI_OK; +} + +int cmd_rtp_xten (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + struct mops_ext_rtp * pd = clipkt->p_desc; + char state[8]; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc!=1)) { + cli_print(cli, "Sets or unsets the RTP extension flag (default: disabled).\n"); + cli_print(cli, "NOTE: This command only sets the extension flag in the RTP header.\r"); + cli_print(cli, "If you really want an extension header use the 'extension' command.\n"); + cli_print(cli, "Use the keywords 'set' or 'unset'.\n"); + sprintf(state, "%s", (pd->x) ? "SET" : "UNSET"); + cli_print(cli, "Current state of the extension flag: %s\n",state); + return CLI_OK; + } + + if (mz_strcmp(argv[0], "set", 1)==0) { + pd->x = 1; + } else if (mz_strcmp(argv[0], "unset", 1)==0) { + pd->x = 0; + } + else { + cli_print(cli, "Invalid keyword. Use 'set' or 'unset'.\n"); + } + + return CLI_OK; +} + + +int cmd_rtp_marker (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + struct mops_ext_rtp * pd = clipkt->p_desc; + char state[8]; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc!=1)) { + cli_print(cli, "Sets or unsets the RTP marker flag (default: disabled).\n"); + cli_print(cli, "Use the keywords 'set' or 'unset'.\n"); + sprintf(state, "%s", (pd->m) ? "SET" : "UNSET"); + cli_print(cli, "Current state of the marker flag: %s\n",state); + return CLI_OK; + } + if (mz_strcmp(argv[0], "set", 1)==0) { + pd->m = 1; + } else if (mz_strcmp(argv[0], "unset", 1)==0) { + pd->m = 0; + } + else { + cli_print(cli, "Invalid keyword. Use 'set' or 'unset'.\n"); + } + return CLI_OK; +} + + +int cmd_rtp_cc (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + struct mops_ext_rtp * pd = clipkt->p_desc; + int cc=0; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc!=1)) { + cli_print(cli, "Configure the RTP CSRC count (0..15, default: 0).\n"); + cli_print(cli, "NOTE: This command only configures the CSRC value in the RTP header.\r"); + cli_print(cli, "If you want to add a valid CSRC list use the 'csrc-list' command.\r"); + cli_print(cli, "The main purpose of this command is to create an invalid RTP packet.\r"); + return CLI_OK; + } + if (mz_strisnum(argv[0])==0) { + cli_print(cli, "Invalid number.\n"); + return CLI_OK; + } + cc = (int) str2int(argv[0]); + if (cc>15) { + cli_print(cli, "Range exceeded (0..15).\n"); + return CLI_OK; + } + pd->cc = cc; + return CLI_OK; +} + + +int cmd_rtp_pt (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + struct mops_ext_rtp * pd = clipkt->p_desc; + int pt=0; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc!=1)) { + cli_print(cli, "Configure the RTP payload type (0..127, default: 8 (G.711, A-law)).\n"); + // TODO: provide a list with well-known PT values + return CLI_OK; + } + if (mz_strisnum(argv[0])==0) { + cli_print(cli, "Invalid number.\n"); + return CLI_OK; + } + pt = (int) str2int(argv[0]); + if (pt>127) { + cli_print(cli, "Range exceeded (0..127).\n"); + return CLI_OK; + } + pd->pt = pt; + return CLI_OK; +} + +int cmd_rtp_ssrc (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + struct mops_ext_rtp * pd = clipkt->p_desc; + unsigned long long int ssrc = 0xcafefeed; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc!=1)) { + cli_print(cli, "Configure the RTP SSRC (source identifier) (0..ffffffff, default: random!).\n"); + cli_print(cli, "NOTE: The SSRC value is used by another Mausezahn receiver to identify a original\r"); + cli_print(cli, "Mausezahn RTP stream. By default, Mausezahn receivers check for the magic number\r"); + cli_print(cli, "'cafebabe' (hex). Use another number for another RTP stream (e. g. bidirectional\r"); + cli_print(cli, "measurements).\n"); + return CLI_OK; + } + + if (mz_strishex(argv[0])==0) { + cli_print(cli, "Invalid number.\n"); + return CLI_OK; + } + + ssrc = xstr2lint(argv[0]); + if (ssrc>0xffffffff) { + cli_print(cli, "Range exceeded (0..ffffffff).\n"); + return CLI_OK; + } + pd->ssrc = (u_int32_t) ssrc; + return CLI_OK; +} + + +int cmd_rtp_sqnr (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + struct mops_ext_rtp * pd = clipkt->p_desc; + unsigned long long int sqnr = 0; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc!=1)) { + cli_print(cli, "Configure the RTP initial sequence number (0..ffffffff, default: 0).\n"); + return CLI_OK; + } + + if (mz_strishex(argv[0])==0) { + cli_print(cli, "Invalid number.\n"); + return CLI_OK; + } + sqnr = xstr2lint(argv[0]); + if (sqnr>0xffffffff) { + cli_print(cli, "Range exceeded (0..ffffffff).\n"); + return CLI_OK; + } + pd->sqnr = (u_int32_t) sqnr; + return CLI_OK; +} + + +int cmd_rtp_time (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + struct mops_ext_rtp * pd = clipkt->p_desc; + unsigned long long int t = 0; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc!=1)) { + cli_print(cli, "Configure the RTP initial timestamp (0..ffffffff, default: 0).\n"); + return CLI_OK; + } + + if (mz_strishex(argv[0])==0) { + cli_print(cli, "Invalid number.\n"); + return CLI_OK; + } + t = xstr2lint(argv[0]); + if (t>0xffffffff) { + cli_print(cli, "Range exceeded (0..ffffffff).\n"); + return CLI_OK; + } + pd->tst = (u_int32_t) t; + return CLI_OK; +} + + +int cmd_rtp_extension (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + struct mops_ext_rtp * pd = clipkt->p_desc; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc!=1)) { + cli_print(cli, "Configure an RTP extension header (default: none).\n"); + cli_print(cli, "Currently supported RTP extension headers:\n"); + cli_print(cli, "none Don't use any extension.\r"); + cli_print(cli, "mausezahn Use the new Mausezahn jitter/RTT measurement extension.\r"); + cli_print(cli, " (Note that this is incompatible with Mausezahn's direct\r"); + cli_print(cli, " mode jitter measurement.)\r"); + cli_print(cli, "\n"); + return CLI_OK; + } + + if (mz_strcmp(argv[0], "none", 1)==0) { + pd->x_type = 0; + pd->x = 0; // X bit in header + } else if (mz_strcmp(argv[0], "mausezahn", 1)==0) { + pd->x_type = 42; + pd->x = 1; // X bit in header + pd->ssrc = 0xcafefeed; + } else { + cli_print(cli, "Unknown keyword.\n"); + return CLI_OK; + + } + + mops_update_rtp (clipkt); // re-build RTP packet (for proper show commands) + return CLI_OK; +} + + + +int cmd_rtp_source (struct cli_def *cli, const char *command, char *argv[], int argc) +{ +// struct mops_ext_rtp * pd = clipkt->p_desc; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc!=1)) { + cli_print(cli, "Specify a RTP media source.\n"); + return CLI_OK; + } + + // [TODO] -- Allow to use /dev/dsp or a mixer source ... + // + cli_print(cli, "Currently not supported.\n"); + + return CLI_OK; +} + + +int cmd_rtp_cclist (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + struct mops_ext_rtp * pd = clipkt->p_desc; + unsigned long long int csrc=0; + char str[80]; + int i=0, n=0; + + + if ((strcmp(argv[argc-1],"?")==0) || (argc==0)) { + cli_print(cli, "Specify a CSRC list consisting of 1-15 CSRC values.\r"); + cli_print(cli, "Each CSRC is a 4-byte value and must be specified in hexadecimal notation,\r"); + cli_print(cli, "hence each value must be within 0..ffffffff.\n"); + return CLI_OK; + } + + if ((n=argc)>15) { + cli_print(cli, "The CSRC list must not exceed 15 items!\n"); + return CLI_OK; + } + + for (i=0; i<n; i++) { + if (mz_strishex(argv[i])==0) { + sprintf(str, "Parameter %i: Invalid number!", i); + cli_print(cli, "%s\n", str); + return CLI_OK; + } + csrc = xstr2lint(argv[i]); + if (csrc>0xffffffff) { + sprintf(str, "Parameter %i: Range exceeded (0..ffffffff)", i); + cli_print(cli, "%s\n", str); + return CLI_OK; + } + pd->csrc[i] = (u_int32_t) csrc; + } + pd->cc = n; // this one can be accessed and modified to "wrong" values by the user + pd->cc_real = n; + + return CLI_OK; +} + + + diff --git a/staging/cli_sequence.c b/staging/cli_sequence.c new file mode 100644 index 0000000..0a55251 --- /dev/null +++ b/staging/cli_sequence.c @@ -0,0 +1,263 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 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 + * +*/ + + +#include "mz.h" +#include "cli.h" +#include "mops.h" +#include "llist.h" + +// PURPOSE: Enter sequence configuration mode +// either a) create new or b) edit old or c) delete old sequence +// +// # sequence MY_SEQUENCE +// # sequence OLD_SEQUENCE delete +// +int conf_sequence (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + struct mz_ll *cur; + char str[512]; + int ret=0; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc<1) || (argc>2)) { + + cli_print(cli, "Configure a sequence of packets.\n"); + cli_print(cli, "ARGUMENTS: <sequence_name> [delete]\n"); + + cli_print(cli, "Current list of packet sequences:\n"); + while (mops_dump_sequence(str)) cli_print(cli, "%s\r", str); + return CLI_OK; + } + + switch (argc) { + case 1: + cur = mz_ll_search_name (packet_sequences, argv[0]); + if (cur==NULL) { // create NEW sequence + cli_print(cli, "Sequence does not exist; creating new sequence named '%s'\n", argv[0]); + cur = mops_create_sequence(argv[0]); + if (cur==NULL) { + cli_print(cli, "ERROR: Cannot allocate another sequence!\n"); + return CLI_OK; + } + } // else ENTER EXISTING (cur already points to it) + cli_seq = cur; + cli_set_configmode(cli, MZ_MODE_SEQUENCE, "config-seq"); + break; + + case 2: // otherwise DELETE? + if (mz_strcmp(argv[1], "delete", 3)==0) { + ret = mops_delete_sequence(argv[0]); + switch (ret) { + case 1: + cli_print(cli, "Sequence '%s' does not exist\n", argv[0]); + break; + case 2: + cli_print(cli, "Sequence '%s' is currently active! Cannot delete it.\n", argv[0]); + break; + default: + cli_print(cli, "Sequence '%s' deleted.\n", argv[0]); + } + } + break; + default: + // nothing + break; + } + return CLI_OK; +} + + +// add packet to current sequence +int sequence_add (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + struct mops *mp; + int ret=0; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc!=1) ) { + + cli_print(cli, "Add a packet to the current sequence.\n"); + cli_print(cli, "ARGUMENT: <packet name> OR <packet-identifier>\n"); + return CLI_OK; + } + + // first assume argument is a name + mp = mops_search_name (mp_head, argv[0]); + if (mp==NULL) { // but packet name does not exist + if (mz_strisnum(argv[0])!=0) // arg is really a number? + mp = mops_search_id (mp_head, (int) str2int(argv[0])); + if (mp==NULL) { // also packet ID not found + cli_print(cli, "Packet does not exist!\n"); + return CLI_OK; + } + } + + // packet found, so add to current sequence + ret = mops_add_packet_to_sequence (cli_seq, mp); + if (ret==1) cli_print(cli, "Cannot add packet (unknown error, maybe report this)!\n"); + if (ret==-1) cli_print(cli, "Cannot add packet: sequence already full!\n"); + if (ret==-2) cli_print(cli, "Cannot add packet with infinite count!\n"); + return CLI_OK; +} + + +// add a delay +int sequence_delay (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + int ret=0, ret2=0; + struct timespec t; + char str[128]; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc<1) || (argc>2)) { + cli_print(cli, "Add a delay to the current sequence.\n"); + cli_print(cli, "ARGUMENTS: <delay> [hour | min | sec | msec | usec | nsec]\n"); + cli_print(cli, "The default unit is milliseconds (i. e. when no unit is given).\n"); + return CLI_OK; + } + + switch (argc) { + case 1: // only one argument, but may contain an unit (such as '314sec') + ret = delay_parse(&t, argv[0], NULL); + break; + + case 2: // user specified two arguments such as '100 msec' + ret = delay_parse(&t, argv[0], argv[1]); + break; + default: + cli_print(cli, "Too many arguments! Expected delay value and unit, such as '10 msec'\n"); + return CLI_OK; + } + + switch (ret) { + case 1: + cli_print(cli, "Invalid unit! Use one of {nsec, usec, msec, sec, min, hours}\n"); + return CLI_OK; + break; + case 2: + cli_print(cli, "Value too large! Supported range is from 0 to 999999999\n"); + return CLI_OK; + break; + } + + + ret2 = mops_add_delay_to_sequence (cli_seq, &t); + if (ret2==-1) { + cli_print(cli, "You must add a packet first.\n"); + return CLI_OK; + } + if (ret2==-2) { + cli_print(cli, "Cannot add delay (array full).\n"); + return CLI_OK; + } + + sprintf(str, "Delay set to %lu sec and %lu nsec", + ((struct pseq*) cli_seq->data)->gap[ret2].tv_sec, + ((struct pseq*) cli_seq->data)->gap[ret2].tv_nsec); + cli_print(cli, "%s\n", str); + + return CLI_OK; +} + + +// remove one packet +int sequence_remove (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + int ret=0; + int i=0; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc!=1)) { + cli_print(cli, "Remove a packet (and any associated pause configuration) from the current sequence.\n"); + cli_print(cli, "ARGUMENT: <sequence-list-index> | last | all\n"); + cli_print(cli, "FYI: Use the 'show' command to see the current packet list with indexes.\n"); + return CLI_OK; + } + + if (mz_strcmp(argv[0], "last", 1)==0) { + ret = mops_delete_packet_from_pseq (cli_seq, -1); + } else if (mz_strcmp(argv[0], "all", 1)==0) { + ret = mops_delete_all_packets_from_pseq (cli_seq); + i=1; + } else { // index number given + if (mz_strisnum(argv[0])==0) { + cli_print(cli, "Invalid parameter. Please specify a packet index number or 'last'\n"); + return CLI_OK; + } + ret = mops_delete_packet_from_pseq (cli_seq, (int) str2int(argv[0])); + } + switch (ret) { + case 0: + if (i) cli_print(cli, "Removed all entries.\n"); + else cli_print(cli, "Removed one entry.\n"); + break; + case 1: + cli_print(cli, "List empty or invalid packet index.\n"); + break; + case 2: + cli_print(cli, "Packet index too large.\n"); + break; + + } + return CLI_OK; +} + + +// show packet list of that sequence +int sequence_show (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + char str[512], name[32], layers[16], proto[16]; + struct pseq *seq; + int i; + + if (strcmp(argv[argc-1],"?")==0) { + cli_print(cli, "Shows all packets of the current sequence.\n"); + return CLI_OK; + } + + if (argc>0) { + cli_print(cli, "This command has currently no arguments!\n"); + return CLI_OK; + } + + seq = (struct pseq*) cli_seq->data; + + if (seq->count==0) { + cli_print(cli, "Current sequence '%s' has no entries.\n", cli_seq->name); + } + else { // show all packets in this sequence + cli_print(cli, "%i sequence(s) defined.\r", packet_sequences->refcount-1); // total info + snprintf(str,512, "Current sequence '%s' has %i entries:", cli_seq->name, seq->count); // num entries here + cli_print(cli, "%s\n", str); + cli_print(cli, "Nr PId PktName Layers Protocol Device"); + for (i=0; i<seq->count; i++) { + strncpy (name, seq->packet[i]->packet_name, 13); // only show first 13 chars + if (strnlen(seq->packet[i]->packet_name, MAX_MOPS_PACKET_NAME_LEN)>13) { + name[13]=0x00; + strcat(name, "..."); + } + mops_get_proto_info(seq->packet[i], layers, proto); + snprintf(str,512, "%2i %4i %-16s %s %-8s %-6s", i+1, seq->packet[i]->id, name, layers, proto, seq->packet[i]->device); + cli_print(cli, "%s\r", str); + if ((seq->gap[i].tv_sec !=0) || (seq->gap[i].tv_nsec !=0)) { // gap also defined? + timespec2str(&seq->gap[i], str); + cli_print(cli, " \\___ %s pause ___/\r", str); + } + } + } + return CLI_OK; +} + + diff --git a/staging/cli_set.c b/staging/cli_set.c new file mode 100644 index 0000000..8b365fc --- /dev/null +++ b/staging/cli_set.c @@ -0,0 +1,350 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008 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 + * +*/ + + +#include "mz.h" +#include "cli.h" + + +int cmd_set(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + libnet_t *l; + unsigned int time_factor; + int i, cnt, found_dev; + char *dum; + unsigned char *x; + + + if (argc < 2) { + cli_print(cli, "Specify a variable to set:\r\n"); + cli_print(cli, "device specify the primary network device\r"); + + cli_print(cli, "NOTE: The following options are non-MOPS and deprecated:\n"); + + cli_print(cli, "a|sa specify a MAC source address\r"); + cli_print(cli, "b|da specify a MAC destination address\r"); + cli_print(cli, "A|SA specify a IP source address\r"); + cli_print(cli, "B|DA specify a IP destination address\r"); + cli_print(cli, "c|count specify a packet count value\r"); + cli_print(cli, "d|delay specify an interpacket delay (usec, msec, or sec)\r"); + cli_print(cli, "P|payload specify an ASCII payload\r"); + cli_print(cli, "H|hexload specify a hexadecimal payload\r"); + cli_print(cli, "p|padding specify a number of padding bytes (total for raw, added otherwise)\r"); + cli_print(cli, "Q|vlan specify one ore more 802.1Q vlan tags\r"); + cli_print(cli, "M|mpls specify one ore more MPLS labels\r"); + cli_print(cli, "\n"); + return CLI_OK; + } + + // set primary device + if (strncmp(argv[0], "device", 2)==0) + { + if (strncmp(argv[1],"?",1)==0) + { + cli_print(cli,"Specify the primary network device (use 'show dev' for a list)\n"); + } + else + { + if (strlen(argv[1])) + { + found_dev = 0; + for (i=0; i<device_list_entries; i++) + { + if (strncmp(device_list[i].dev, argv[1], 16)==0) + { + found_dev=1; + break; + } + } + if (found_dev) + { + strncpy(tx.device, argv[1], 16); + } + else + cli_print(cli, "Unknown device, will stick on %s\n", tx.device); + } + else + cli_print(cli, "Nothing specified, will stick on %s\n", tx.device); + } + } + + + // set source MAC address + else if ( (strncmp(argv[0], "a", 10)==0) || + (strncmp(argv[0], "sa", 10)==0) ) + { + if (strncmp(argv[1],"?",1)==0) + { + cli_print(cli,"Specify a source MAC address (format: XX:XX:XX:XX:XX:XX)\n"); + } + else + { + strncpy(tx.eth_src_txt, argv[1], 32); + if (check_eth_mac_txt(ETH_SRC)) + { + cli_print(cli, "Invalid MAC address! Format: XX:XX:XX:XX:XX:XX\r"); + cli_print(cli, "Current setting: sa = %02x:%02x:%02x:%02x:%02x:%02x\r", + tx.eth_src[0], tx.eth_src[1], tx.eth_src[2], + tx.eth_src[3], tx.eth_src[4], tx.eth_src[5]); + } + + tx.packet_mode = 0; + + } + } + + // set destination MAC address + else if ( (strncmp(argv[0], "b", 10)==0) || + (strncmp(argv[0], "da", 10)==0) ) + { + if (strncmp(argv[1],"?",1)==0) + { + cli_print(cli,"Specify a destination MAC address (format: XX:XX:XX:XX:XX:XX)\n"); + } + else + { + strncpy(tx.eth_dst_txt, argv[1], 32); + if (check_eth_mac_txt(ETH_DST)) + { + cli_print(cli, "Invalid MAC address! Format: XX:XX:XX:XX:XX:XX\r"); + cli_print(cli, "Current setting: da = %02x:%02x:%02x:%02x:%02x:%02x\r", + tx.eth_dst[0], tx.eth_dst[1], tx.eth_dst[2], + tx.eth_dst[3], tx.eth_dst[4], tx.eth_dst[5]); + } + + tx.packet_mode = 0; + } + } + + // set source IP address + else if ( (strncmp(argv[0], "A", 10)==0) || + (strncmp(argv[0], "SA", 10)==0) ) + { + if (strncmp(argv[1],"?",1)==0) + { + cli_print(cli,"Specify a source IP address, a FQDN, 'rand', or a range\n"); + } + else + { + if (strcmp(argv[1], "rand") == 0) + { + tx.ip_src_rand = 1; + tx.ip_src_h = (u_int32_t) ( ((float) rand()/RAND_MAX)*0xE0000000); //this is 224.0.0.0 +// TODO: mops_hton32 (&tx.ip_src_h, &tx.ip_src); + } + else if (get_ip_range_src(argv[1])) // returns 1 when no range has been specified + { + l = libnet_init (LIBNET_LINK_ADV, tx.device, NULL); + if (l == NULL) + { + cli_print(cli, "Error: could not access the network device!\n"); + return CLI_OK; + } + // name2addr4 accepts a DOTTED DECIMAL ADDRESS or a FQDN: + tx.ip_src = libnet_name2addr4 (l, argv[1], LIBNET_RESOLVE); + x = (unsigned char *) &tx.ip_src; + cli_print(cli, "Set source IP address to %i.%i.%i.%i\n", + *x,*(x+1),*(x+2),*(x+3)); +// TODO: mops_hton32 (&tx.ip_src, &tx.ip_src_h); + libnet_destroy(l); + } + } + } + + // set destination IP address + else if ( (strncmp(argv[0], "B", 10)==0) || + (strncmp(argv[0], "DA", 10)==0) ) + { + if (strncmp(argv[1],"?",1)==0) + { + cli_print(cli,"Specify a destination IP address, a FQDN, or a range\n"); + } + else + { + if (get_ip_range_dst(argv[1])) // returns 1 when no range has been specified + { + l = libnet_init (LIBNET_LINK_ADV, tx.device, NULL); + if (l == NULL) + { + cli_print(cli, "Error: could not access the network device!\n"); + return CLI_OK; + } + // name2addr4 accepts a DOTTED DECIMAL ADDRESS or a FQDN: + tx.ip_dst = libnet_name2addr4 (l, argv[1], LIBNET_RESOLVE); + x = (unsigned char *) &tx.ip_src; + cli_print(cli, "Set destination IP address to %i.%i.%i.%i\n", + *x,*(x+1),*(x+2),*(x+3)); +// TODO: mops_hton32 (&tx.ip_dst, &tx.ip_dst_h); + libnet_destroy(l); + } + } + } + + // set packet count + else if ( (strncmp(argv[0], "c", 10)==0) || + (strncmp(argv[0], "count", 10)==0) ) + { + if (strncmp(argv[1],"?",1)==0) + { + cli_print(cli,"Specify a packet count value\n"); + } + else + { + cnt = (unsigned int) str2int (argv[1]); + if (cnt==0) + { + cli_print(cli, "Warning: A packet count of zero means an infinite number of packets.\r"); + cli_print(cli, "Infinite packets are only supported via MOPS (use the 'packet' command\r"); + cli_print(cli, "in global configuration mode) or when running Mausezahn from the shell.\n"); + cli_print(cli, "Note: The count value has NOT been changed.\n"); + } + else + { + tx.count = cnt; + } + } + } + + // set interpacket delay + else if ( (strncmp(argv[0], "d", 10)==0) || + (strncmp(argv[0], "delay", 10)==0) ) + { + if (strncmp(argv[1],"?",1)==0) + { + cli_print(cli,"Specify an interpacket delay (usec, msec, or sec)\n"); + } + else + { + // determine whether seconds or msecs are used + // default is usec!!! + time_factor=1; + if (exists(argv[1],"s") || exists(argv[1],"sec")) time_factor=1000000; + if (exists(argv[1],"m") || exists(argv[1],"msec")) time_factor=1000; + dum = strtok(argv[1],"ms"); + tx.delay = strtol(dum, (char **)NULL, 10) * time_factor; + if ((errno == ERANGE && (tx.delay == LONG_MAX || tx.delay == LONG_MIN)) + || (errno != 0 && tx.delay == 0)) + { + cli_print(cli, "Value out of range!\n"); + } + if (tx.delay<0) tx.delay=0; // no delay + + cli_print(cli, "Set interpacket delay to %u usec\n", tx.delay); + } + + } + + // set ASCII payload + else if ( (strncmp(argv[0], "P", 10)==0) || + (strncmp(argv[0], "payload", 10)==0) ) + { + if (strncmp(argv[1],"?",1)==0) + { + cli_print(cli,"Specify an ASCII payload enclosed in quotes\n"); + } + else + { + strncpy((char *)tx.ascii_payload, argv[1], MAX_PAYLOAD_SIZE); + tx.ascii=1; + } + } + + + // set HEX payload + else if ( (strncmp(argv[0], "H", 10)==0) || + (strncmp(argv[0], "hexload", 10)==0) ) + { + if (strncmp(argv[1],"?",1)==0) + { + cli_print(cli,"Specify a hexadecimal payload (using ':' or '.' as delimiters)\n"); + } + else + { + tx.hex_payload_s = str2hex (argv[1], tx.hex_payload, 8192); + if (tx.hex_payload_s==0) + cli_print(cli, "Invalid hexadecimal string. Try something like aa:bb:cc:45:99:00:de:ad:be:ef: ...\n"); + } + } + + + // set MPLS labels + else if ( (strncmp(argv[0], "M", 10)==0) || + (strncmp(argv[0], "mpls", 10)==0) ) + { + if (strncmp(argv[1],"?",1)==0) + { + cli_print(cli,"Specify one or more MPLS labels\n"); + } + else + { + if (strlen(argv[1])) // TODO: Better verification of 802.1Q syntax + { + strncpy(tx.mpls_txt, argv[1], 128); + tx.mpls=1; + } + } + } + + + // set 802.1Q tags + else if ( (strncmp(argv[0], "Q", 10)==0) || + (strncmp(argv[0], "vlan", 10)==0) ) + { + if (strncmp(argv[1],"?",1)==0) + { + cli_print(cli,"Specify one or more 802.1Q VLAN tags (and optionally 801.1P values)\n"); + } + else + { + if (strlen(argv[1])) // TODO: Better verification of 802.1Q syntax + { + strncpy(tx.dot1Q_txt, argv[1], 32); + tx.dot1Q=1; + } + } + } + + + // set padding + else if ( (strncmp(argv[0], "p", 10)==0) || + (strncmp(argv[0], "padding", 10)==0) ) + { + if (strncmp(argv[1],"?",1)==0) + { + cli_print(cli,"Specify a number of padding bytes\n"); + } + else + { + tx.padding = (unsigned int) str2int(argv[1]); + if (tx.padding > MAX_PAYLOAD_SIZE) + { + cli_print(cli, "Note: Padding too big! However, let's try and see what happens...\n"); + } + } + } + + + + // DEFAULT ANSWER: + else + { + cli_print(cli, "Unknown variable '%s'\n",argv[0]); + } + + return CLI_OK; +} diff --git a/staging/cli_tcp.c b/staging/cli_tcp.c new file mode 100644 index 0000000..16dc5a0 --- /dev/null +++ b/staging/cli_tcp.c @@ -0,0 +1,679 @@ +/* + * 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 + * +*/ + + +#include "mz.h" +#include "cli.h" +#include "mops.h" + + + +// NOTE: The port numbers are maintained for both TCP and UDP. +// See cli_udp.c. + + +int cmd_tcp_seqnr (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + u_int32_t txs; + unsigned long long int tmp; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>3) ) { + cli_print(cli, "Specify the TCP sequence number (0-4294967295)\n"); + cli_print(cli, "You may specify up to three parameters:\n"); + cli_print(cli, " <sqnr>\r"); + cli_print(cli, " <sqnr_start> <sqnr_stop>\r"); + cli_print(cli, " <sqnr_start> <sqnr_stop> <sqnr_delta>\n"); + cli_print(cli, "If a range is specified without step size 'sqnr_delta' (2nd case)\r"); + cli_print(cli, "then sqnr_delta is per default set to one.\r"); + cli_print(cli, "\n"); + return CLI_OK; + } + + tmp = str2lint(argv[0]); + if (tmp<=0xffffffff) + clipkt->tcp_seq = (u_int32_t) tmp; + else { + cli_print(cli, "Argument 1 must not exceed 4294967295\n"); + return CLI_OK; + } + clipkt->tcp_seq_delta = 0; + + if (argc>1) { + tmp = str2lint(argv[1]); + if (tmp<=0xffffffff) { + clipkt->tcp_seq_start = clipkt->tcp_seq; + clipkt->tcp_seq_stop = (u_int32_t) tmp; + } else { + cli_print(cli, "Argument 2 must not exceed 4294967295\n"); + return CLI_OK; + } + clipkt->tcp_seq_delta = 1; + } + + if (argc>2) { + tmp = str2lint(argv[2]); + if (tmp<=0xffffffff) { + clipkt->tcp_seq_delta = (u_int32_t) tmp; + } else { + cli_print(cli, "Argument 3 must not exceed 4294967295\n"); + return CLI_OK; + } + + if (argv[2]==0) { + cli_print(cli, "Note that a zero step size disables the range feature\n"); + return CLI_OK; + } + } + + txs = mops_tcp_complexity_sqnr (clipkt); + cli_print(cli, "FYI: Packet runs through %lu sequence numbers\n", (long unsigned int) txs); + + return CLI_OK; +} + + + + + +int cmd_tcp_acknr (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + u_int32_t txs; + unsigned long long int tmp; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>3) ) { + cli_print(cli, "Specify the TCP acknowledgement number (0-4294967295)\n"); + cli_print(cli, "You may specify up to three parameters:\n"); + cli_print(cli, " <acknr>\r"); + cli_print(cli, " <acknr_start> <acknr_stop>\r"); + cli_print(cli, " <acknr_start> <acknr_stop> <acknr_delta>\n"); + cli_print(cli, "If a range is specified without step size 'acknr_delta' (2nd case)\r"); + cli_print(cli, "then acknr_delta is per default set to one.\r"); + cli_print(cli, "\n"); + return CLI_OK; + } + + tmp = str2lint(argv[0]); + if (tmp<=0xffffffff) + clipkt->tcp_ack = (u_int32_t) tmp; + else { + cli_print(cli, "Argument 1 must not exceed 4294967295\n"); + return CLI_OK; + } + clipkt->tcp_ack_delta = 0; + + if (argc>1) { + tmp = str2lint(argv[1]); + if (tmp<=0xffffffff) { + clipkt->tcp_ack_start = clipkt->tcp_ack; + clipkt->tcp_ack_stop = (u_int32_t) tmp; + } else { + cli_print(cli, "Argument 2 must not exceed 4294967295\n"); + return CLI_OK; + } + clipkt->tcp_ack_delta = 1; + } + + if (argc>2) { + tmp = str2lint(argv[2]); + if (tmp<=0xffffffff) { + clipkt->tcp_ack_delta = (u_int32_t) tmp; + } else { + cli_print(cli, "Argument 3 must not exceed 4294967295\n"); + return CLI_OK; + } + + if (argv[2]==0) { + cli_print(cli, "Note that a zero step size disables the range feature\n"); + return CLI_OK; + } + } + + txs = mops_tcp_complexity_acknr (clipkt); + cli_print(cli, "FYI: Packet runs through %lu acknowledge numbers\n", (long unsigned int) txs); + + + return CLI_OK; +} + + + + + + +int cmd_tcp_offset (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + unsigned int tmp; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>1) ) + { + cli_print(cli, "Specify the TCP offset (=header length, 0..15) \r"); + cli_print(cli, "\n"); + return CLI_OK; + } + + tmp = (unsigned int) str2int(argv[0]); + if (tmp<=15) + clipkt->tcp_offset = (u_int8_t) tmp; + else + { + cli_print(cli, "The TCP offset must not exceed 15\n"); + } + + return CLI_OK; +} + + + + +int cmd_tcp_res (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + int tmp; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>1) ) + { + cli_print(cli, "Specify the TCP reserved field in binary format (4 bits)\n"); + cli_print(cli, "\n"); + return CLI_OK; + } + + tmp = str2bin8 (argv[0]); + if ((tmp==-1)||(tmp>15)) + { + cli_print(cli, "Invalid binary value! Allowed range: 0000 - 1111\n"); + } + else + clipkt->tcp_res = (u_int8_t) tmp; + + return CLI_OK; +} + + +int cmd_tcp_flags (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + int i, j=0; + char str[64]; + + if (strcmp(argv[argc-1],"?")==0) + { + cli_print(cli, "Configure a combination of TCP flags at once. All mentioned \r"); + cli_print(cli, "flags are set, while not mentioned flags remain unset.\r"); + cli_print(cli, "Flag keywords: cwr, ece, urg, ack, psh, rst, syn, fin.\r"); + cli_print(cli, "NOTE: The flags command alone resets all flags to zero!\n"); + cli_print(cli, "Example:\n"); + cli_print(cli, " mz(config-pkt-1-tcp)# flags syn fin ack \n"); + cli_print(cli, "\n"); + mops_tcp_flags2str (clipkt, str); + cli_print(cli,"Current setting is: %s\n",str); + return CLI_OK; + } + + if (argc>8) + { + cli_print(cli, "Up to 8 arguments are allowed using the keywords:\r"); + cli_print(cli, "cwr, ece, urg, ack, psh, rst, syn, fin.\n"); + return CLI_OK; + } + + clipkt->tcp_ctrl_CWR = 0; + clipkt->tcp_ctrl_ECE = 0; + clipkt->tcp_ctrl_URG = 0; + clipkt->tcp_ctrl_ACK = 0; + clipkt->tcp_ctrl_PSH = 0; + clipkt->tcp_ctrl_RST = 0; + clipkt->tcp_ctrl_SYN = 0; + clipkt->tcp_ctrl_FIN = 0; + + + + for (i=0; i<argc; i++) { + if (mz_strcmp(argv[i], "cwr", 1)==0) { + clipkt->tcp_ctrl_CWR = 1; + j=1; + } + + if (mz_strcmp(argv[i], "ece", 1)==0) { + clipkt->tcp_ctrl_ECE = 1; + j=1; + } + + if (mz_strcmp(argv[i], "urg", 1)==0) { + clipkt->tcp_ctrl_URG = 1; + j=1; + } + + if (mz_strcmp(argv[i], "ack", 1)==0) { + clipkt->tcp_ctrl_ACK = 1; + j=1; + } + + if (mz_strcmp(argv[i], "psh", 1)==0) { + clipkt->tcp_ctrl_PSH = 1; + j=1; + } + + if (mz_strcmp(argv[i], "rst", 1)==0) { + clipkt->tcp_ctrl_RST = 1; + j=1; + } + + if (mz_strcmp(argv[i], "syn", 1)==0) { + clipkt->tcp_ctrl_SYN = 1; + j=1; + } + + if (mz_strcmp(argv[i], "fin", 1)==0) { + clipkt->tcp_ctrl_FIN = 1; + j=1; + } + + if (!j) { + cli_print(cli, "Unknown keyword at position %i\n", i+1); + return CLI_OK; + } + else { // flag matched, continue + j=0; + } + } + + mops_tcp_flags2str (clipkt, str); + cli_print(cli,"Current setting is: %s\n",str); + + return CLI_OK; +} + + + +int cmd_tcp_cwr (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + char str[64]; + + if (strcmp(argv[argc-1],"?")==0) + { + cli_print(cli, "Set or unset the TCP Congestion Window Reduced flag (CWR)\r"); + mops_tcp_flags2str (clipkt, str); + cli_print(cli,"Current setting is: %s\n",str); + return CLI_OK; + } + + if (argc!=1) + { + cli_print(cli, "Use the 'set' or 'unset' keywords.\n"); + return CLI_OK; + } + + + if (mz_strcmp(argv[0], "set", 1)==0) + clipkt->tcp_ctrl_CWR = 1; + else if (mz_strcmp(argv[0], "unset", 1)==0) + clipkt->tcp_ctrl_CWR = 0; + else + cli_print(cli, "Unknown keyword. Use the 'set' or 'unset' keywords.\n"); + + mops_tcp_flags2str (clipkt, str); + cli_print(cli,"Current setting is: %s\n",str); + + return CLI_OK; +} + + + +int cmd_tcp_ece (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + char str[64]; + + if (strcmp(argv[argc-1],"?")==0) { + cli_print(cli, "Set or unset the TCP ECN-Echo flag (ECE)\r"); + mops_tcp_flags2str (clipkt, str); + cli_print(cli,"Current setting is: %s\n",str); + return CLI_OK; + } + + if (argc!=1) { + cli_print(cli, "Use the 'set' or 'unset' keywords.\n"); + return CLI_OK; + } + + if (mz_strcmp(argv[0], "set", 1)==0) + clipkt->tcp_ctrl_ECE = 1; + else if (mz_strcmp(argv[0], "unset", 1)==0) + clipkt->tcp_ctrl_ECE = 0; + else + cli_print(cli, "Unknown keyword. Use the 'set' or 'unset' keywords.\n"); + + mops_tcp_flags2str (clipkt, str); + cli_print(cli,"Current setting is: %s\n",str); + + return CLI_OK; +} + + + +int cmd_tcp_urg (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + char str[64]; + + if (strcmp(argv[argc-1],"?")==0) { + cli_print(cli, "Set or unset the TCP urgent flag (URG)\r"); + mops_tcp_flags2str (clipkt, str); + cli_print(cli,"Current setting is: %s\n",str); + return CLI_OK; + } + + if (argc!=1) { + cli_print(cli, "Use the 'set' or 'unset' keywords.\n"); + return CLI_OK; + } + + if (mz_strcmp(argv[0], "set", 1)==0) + clipkt->tcp_ctrl_URG = 1; + else if (mz_strcmp(argv[0], "unset", 1)==0) + clipkt->tcp_ctrl_URG = 0; + else + cli_print(cli, "Unknown keyword. Use the 'set' or 'unset' keywords.\n"); + + mops_tcp_flags2str (clipkt, str); + cli_print(cli,"Current setting is: %s\n",str); + + return CLI_OK; +} + + + + +int cmd_tcp_ack (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + char str[64]; + + if (strcmp(argv[argc-1],"?")==0) { + cli_print(cli, "Set or unset the TCP acknowledgement flag (ACK)\r"); + mops_tcp_flags2str (clipkt, str); + cli_print(cli,"Current setting is: %s\n",str); + return CLI_OK; + } + + if (argc!=1) { + cli_print(cli, "Use the 'set' or 'unset' keywords.\n"); + return CLI_OK; + } + + + if (mz_strcmp(argv[0], "set", 1)==0) + clipkt->tcp_ctrl_ACK = 1; + else if (mz_strcmp(argv[0], "unset", 1)==0) + clipkt->tcp_ctrl_ACK = 0; + else + cli_print(cli, "Unknown keyword. Use the 'set' or 'unset' keywords.\n"); + + mops_tcp_flags2str (clipkt, str); + cli_print(cli,"Current setting is: %s\n",str); + + return CLI_OK; +} + + + +int cmd_tcp_psh (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + char str[64]; + + if (strcmp(argv[argc-1],"?")==0) { + cli_print(cli, "Set or unset the TCP push flag (PSH)\r"); + mops_tcp_flags2str (clipkt, str); + cli_print(cli,"Current setting is: %s\n",str); + return CLI_OK; + } + + if (argc!=1) { + cli_print(cli, "Use the 'set' or 'unset' keywords.\n"); + return CLI_OK; + } + + if (mz_strcmp(argv[0], "set", 1)==0) + clipkt->tcp_ctrl_PSH = 1; + else if (mz_strcmp(argv[0], "unset", 1)==0) + clipkt->tcp_ctrl_PSH = 0; + else + cli_print(cli, "Unknown keyword. Use the 'set' or 'unset' keywords.\n"); + + mops_tcp_flags2str (clipkt, str); + cli_print(cli,"Current setting is: %s\n",str); + + return CLI_OK; +} + + + +int cmd_tcp_rst (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + char str[64]; + + if (strcmp(argv[argc-1],"?")==0) { + cli_print(cli, "Set or unset the TCP reset flag (RST)\r"); + mops_tcp_flags2str (clipkt, str); + cli_print(cli,"Current setting is: %s\n",str); + return CLI_OK; + } + + if (argc!=1) { + cli_print(cli, "Use the 'set' or 'unset' keywords.\n"); + return CLI_OK; + } + + if (mz_strcmp(argv[0], "set", 1)==0) + clipkt->tcp_ctrl_RST = 1; + else if (mz_strcmp(argv[0], "unset", 1)==0) + clipkt->tcp_ctrl_RST = 0; + else + cli_print(cli, "Unknown keyword. Use the 'set' or 'unset' keywords.\n"); + + mops_tcp_flags2str (clipkt, str); + cli_print(cli,"Current setting is: %s\n",str); + + return CLI_OK; +} + + + +int cmd_tcp_syn (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + char str[64]; + + if (strcmp(argv[argc-1],"?")==0) { + cli_print(cli, "Set or unset the TCP synchronisation flag (SYN)\r"); + mops_tcp_flags2str (clipkt, str); + cli_print(cli,"Current setting is: %s\n",str); + return CLI_OK; + } + + if (argc!=1) { + cli_print(cli, "Use the 'set' or 'unset' keywords.\n"); + return CLI_OK; + } + + if (mz_strcmp(argv[0], "set", 1)==0) + clipkt->tcp_ctrl_SYN = 1; + else if (mz_strcmp(argv[0], "unset", 1)==0) + clipkt->tcp_ctrl_SYN = 0; + else + cli_print(cli, "Unknown keyword. Use the 'set' or 'unset' keywords.\n"); + + mops_tcp_flags2str (clipkt, str); + cli_print(cli,"Current setting is: %s\n",str); + + return CLI_OK; +} + + + +int cmd_tcp_fin (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + char str[64]; + + if (strcmp(argv[argc-1],"?")==0) { + cli_print(cli, "Set or unset the TCP finalisation flag (FIN)\r"); + mops_tcp_flags2str (clipkt, str); + cli_print(cli,"Current setting is: %s\n",str); + return CLI_OK; + } + + if (argc!=1) { + cli_print(cli, "Use the 'set' or 'unset' keywords.\n"); + return CLI_OK; + } + + if (mz_strcmp(argv[0], "set", 1)==0) + clipkt->tcp_ctrl_FIN = 1; + else if (mz_strcmp(argv[0], "unset", 1)==0) + clipkt->tcp_ctrl_FIN = 0; + else + cli_print(cli, "Unknown keyword. Use the 'set' or 'unset' keywords.\n"); + + mops_tcp_flags2str (clipkt, str); + cli_print(cli,"Current setting is: %s\n",str); + + return CLI_OK; +} + + + +int cmd_tcp_window (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + unsigned long int tmp; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>1) ) + { + cli_print(cli, "Specify the TCP window size (0..65535)\r"); + cli_print(cli, "\n"); + return CLI_OK; + } + + tmp = (unsigned long int) str2int (argv[0]); + if (tmp<65535) + { + clipkt->tcp_win = (u_int16_t) tmp; + } + else + { + cli_print(cli, "The TCP window size must not exceed 65535\n"); + } + + return CLI_OK; +} + + + +int cmd_tcp_sum (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + int sum; + + if (strncmp(argv[argc-1], "?", 2)==0) + { + cli_print(cli, "Specify the TCP checksum in hexadecimal or use the keyword 'auto'.\r"); + cli_print(cli, "By default, the checksum is computed automatically.\n"); + return CLI_OK; + } + + if (mz_strcmp(argv[0], "auto", 2)==0) + { + clipkt->tcp_sum_false=0; + return CLI_OK; + } + + sum = (int) xstr2int(argv[0]); + + if (sum>0xffff) + { + cli_print(cli, "The checksum must be within range 0..ffff\n"); + return CLI_OK; + } + + clipkt->tcp_sum = (u_int16_t) sum; + clipkt->tcp_sum_false=1; + + return CLI_OK; +} + + + +int cmd_tcp_urgptr(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + + unsigned long int tmp; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>1) ) + { + cli_print(cli, "Specify the TCP urgent pointer (0..65535)\r"); + cli_print(cli, "\n"); + return CLI_OK; + } + + tmp = (unsigned long int) str2int (argv[0]); + if (tmp<65535) + { + clipkt->tcp_urg = (u_int16_t) tmp; + } + else + { + cli_print(cli, "The TCP urgent pointer must not exceed 65535\n"); + } + + return CLI_OK; +} + + + +int cmd_tcp_options (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + int mss=0, sack=0, scale=0; + u_int32_t tsval=0, tsecr=0; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>1) ) { + cli_print(cli, "Specify TCP options\n"); + cli_print(cli, "Option parameters:\n"); + cli_print(cli, "[ mss <0..65535> ] [sack] [tsval <0..4294967295> [tsecr <0..4294967295>]] [nop] [scale <0..14>]\n"); + cli_print(cli, "NOTE: Only a set of default options are supported in this version\r"); + cli_print(cli, "(20 bytes, consisting of MSS=1452 bytes, SACK permitted, a Timestamp,\r"); + cli_print(cli, "NOP, and Window Scale 5)\r"); + cli_print(cli, "\n"); + return CLI_OK; + } + + if (clipkt->tcp_option_used) { + // turn off + clipkt->tcp_option_used = 0; + } else { + // turn on + mops_tcp_add_option (clipkt, mss, sack, scale, tsval, tsecr); + + cli_print(cli, "NOTE: Only a set of default options are supported in this version\r"); + cli_print(cli, "(20 bytes, consisting of MSS=1452 bytes, SACK permitted, a Timestamp,\r"); + cli_print(cli, "NOP, and Window Scale 5)\n"); + } + + return CLI_OK; +} + + + +int cmd_tcp_end(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + char prompt[16]; + sprintf(prompt, "pkt-%i",clipkt->id); + cli_set_configmode(cli, MZ_MODE_PACKET, prompt); + return CLI_OK; +} diff --git a/staging/cli_tools.c b/staging/cli_tools.c new file mode 100644 index 0000000..20ce50e --- /dev/null +++ b/staging/cli_tools.c @@ -0,0 +1,40 @@ +/* + * 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 + * +*/ + + + + + +#include "mz.h" + + +// Returns a nice string with default and current value of a given variable +// +// EXAMPLE: +// +// char mystring[256]; +// mz_def16 ("20 seconds", pd->max_age, mystring) +// +int mz_def16 (char *def, u_int16_t val, char *str256) +{ + str256[0]=0x00; + sprintf(str256, "The default value is %s. The current value is %u (0x%04x).", def, val, val); + return 0; +} + + diff --git a/staging/cli_udp.c b/staging/cli_udp.c new file mode 100644 index 0000000..9d29f4c --- /dev/null +++ b/staging/cli_udp.c @@ -0,0 +1,204 @@ +/* + * 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 + * +*/ + + +#include "mz.h" +#include "cli.h" +#include "mops.h" + + + +int cmd_port_source (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + u_int32_t t32=0; + int validport=0; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>2) ) { + cli_print(cli, "Specify the source port number:\n"); + cli_print(cli, " <port> [<end-port>]\r"); + cli_print(cli, " random [norandom]\r"); + cli_print(cli, "\n"); + return CLI_OK; + } + + if (mz_strcmp(argv[0], "random",1)==0) { + clipkt->sp_isrand = 1; + clipkt->sp_isrange = 0; + } else if (mz_strcmp(argv[0], "norandom",1)==0) { + clipkt->sp_isrand = 0; + } else { + if (!mz_strisnum(argv[0])) { + cli_print(cli, "Unknown keyword\n"); + return CLI_OK; + } + t32 = str2int(argv[0]); + if (t32>65535) { + cli_print(cli, "Port number cannot exceed 65535\n"); + return CLI_OK; + } else { + clipkt->sp= (u_int16_t) t32; + validport=1; + clipkt->sp_isrange = 0; + } + } + + if ((argc==2) && (validport)) { + if (!mz_strisnum(argv[1])) { + cli_print(cli, "Invalid number\n"); + return CLI_OK; + } + t32 = str2int(argv[1]); + if (t32>65535) { + cli_print(cli, "Port number cannot exceed 65535\n"); + } else { + clipkt->sp_start = clipkt->sp; + clipkt->sp_stop = (u_int16_t) t32; + clipkt->sp_isrange = 1; + } + } + + return CLI_OK; +} + + + + +int cmd_port_destination (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + u_int32_t t32=0; + int validport=0; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>2) ) { + cli_print(cli, "Specify the destination port number\r"); + cli_print(cli, " <port> [<end-port>]\r"); + cli_print(cli, " random [norandom]\r"); + cli_print(cli, "\n"); + return CLI_OK; + } + + if (mz_strcmp(argv[0], "random",1)==0) { + clipkt->dp_isrand = 1; + clipkt->dp_isrange = 0; + } else if (mz_strcmp(argv[0], "norandom",1)==0) { + clipkt->dp_isrand = 0; + } else { + if (!mz_strisnum(argv[0])) { + cli_print(cli, "Unknown keyword\n"); + return CLI_OK; + + } + t32 = str2int(argv[0]); + if (t32>65535) { + cli_print(cli, "Port number cannot exceed 65535\n"); + return CLI_OK; + } else { + clipkt->dp= (u_int16_t) t32; + validport=1; + clipkt->dp_isrange = 0; + } + } + + if ((argc==2) && (validport)) { + if (!mz_strisnum(argv[1])) { + cli_print(cli, "Invalid number\n"); + return CLI_OK; + } + t32 = str2int(argv[1]); + if (t32>65535) { + cli_print(cli, "Port number cannot exceed 65535\n"); + } else { + clipkt->dp_start = clipkt->dp; + clipkt->dp_stop = (u_int16_t) t32; + clipkt->dp_isrange = 1; + } + } + + return CLI_OK; +} + + + +int cmd_udp_sum (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + int sum; + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>1) ) + { + cli_print(cli, "Specify the UDP checksum:\n"); + cli_print(cli, " - either in hexadecimal format (0-ffff)\r"); + cli_print(cli, " - or use the keyword 'auto' (default)\r"); + cli_print(cli, " - or use the keyword 'unset'\r"); + cli_print(cli, "\r"); + cli_print(cli, "By default, the checksum is computed automatically. The keyword\r"); + cli_print(cli, "'unset' signals the receiver that the checksum has not be computed\r"); + cli_print(cli, "and should be ignored.\n"); + return CLI_OK; + } + + if (mz_strcmp(argv[0], "auto", 2)==0) + { + clipkt->udp_sum_false=0; + return CLI_OK; + } + + if (mz_strcmp(argv[0], "unset", 2)==0) + { + clipkt->udp_sum_false=1; + clipkt->udp_sum = 0xffff; + return CLI_OK; + } + + sum = (int) xstr2int(argv[0]); + + if (sum>0xffff) + { + cli_print(cli, "The checksum must be within range 0..ffff\n"); + return CLI_OK; + } + + clipkt->udp_sum = (u_int16_t) sum; + clipkt->udp_sum_false=1; + + return CLI_OK; +} + + + +int cmd_udp_len (struct cli_def *cli, const char *command, char *argv[], int argc) +{ + + if ( (strcmp(argv[argc-1],"?")==0) || (argc>1) ) + { + cli_print(cli, "Specify the UDP length\r"); + cli_print(cli, "\n"); + return CLI_OK; + } + + cli_print(cli, "Not supported in this version.\n"); + + return CLI_OK; +} + + +int cmd_udp_end(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + char prompt[16]; + sprintf(prompt, "pkt-%i",clipkt->id); + cli_set_configmode(cli, MZ_MODE_PACKET, prompt); + return CLI_OK; +} diff --git a/staging/directmops.c b/staging/directmops.c new file mode 100644 index 0000000..5d95bd3 --- /dev/null +++ b/staging/directmops.c @@ -0,0 +1,30 @@ +/* + * 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 + * +*/ + + +#include "mz.h" +#include "cli.h" +#include "mops.h" + + +int mops_direct(char* dev, int mops_type, char* argstring) +{ + printf("Got device {%s} type {%i} and argstring {%s}\n", dev, mops_type, argstring); + + return 0; +} diff --git a/staging/dns.c b/staging/dns.c new file mode 100644 index 0000000..5f9203c --- /dev/null +++ b/staging/dns.c @@ -0,0 +1,817 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008 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 + * +*/ + + + +//////////////////////////////////////////////////////////////////// +// +// DNS: Only UDP-based here +// +//////////////////////////////////////////////////////////////////// + +#include "mz.h" +#include "cli.h" + + +#define MZ_DNS_HELP \ + "| DNS type: Send Domain Name System Messages.\n" \ + "|\n" \ + "| Generally there are two interesting general DNS messages: queries and answers. The easiest\n" \ + "| way is to use the following syntax:\n" \ + "|\n" \ + "| query|q = <name>[:<type>] ............. where type is per default \"A\"\n" \ + "| (and class is always \"IN\")\n" \ + "|\n" \ + "| answer|a = [<type>:<ttl>:]<rdata> ...... ttl is per default 0.\n" \ + "| = [<type>:<ttl>:]<rdata>/[<type>:<ttl>:]<rdata>/...\n" \ + "|\n" \ + "| Note: If you only use the 'query' option then a query is sent. If you additonally add\n" \ + "| an 'answer' option then an answer is sent.\n" \ + "|\n" \ + "| Examples: \n" \ + "|\n" \ + "| q = www.xyz.com\n" \ + "| q = www.xyz.com, a=192.168.1.10\n" \ + "| q = www.xyz.com, a=A:3600:192.168.1.10\n" \ + "| q = www.xyz.com, a=CNAME:3600:abc.com/A:3600:192.168.1.10\n" \ + "|\n" \ + "| Note: <type> can be: A, CNAME, or any integer\n" \ + "|\n" \ + "|\n" \ + "| OPTIONAL parameter hacks: (if you don't know what you do this might cause invalid packets)\n" \ + "|\n" \ + "| Parameter Description query / reply)\n" \ + "| -------------------------------------------------------------------------------------\n" \ + "|\n" \ + "| request/response|reply ..... flag only request / n.a. \n" \ + "| id ......................... packet id (0-65535) random / random\n" \ + "| opcode (or op) ............. accepts values 0..15 or one of std / 0 \n" \ + "| these keywords: \n" \ + "| = std ................... Standard Query\n" \ + "| = inv ................... Inverse Query\n" \ + "| = sts ................... Server Status Request\n" \ + "| aa or !aa .................. Authoritative Answer UNSET / SET\n" \ + "| tc or !tc .................. Truncation UNSET / UNSET\n" \ + "| rd or !rd .................. Recursion Desired SET / SET\n" \ + "| ra or !ra .................. Recursion Available UNSET / SET\n" \ + "| z .......................... Reserved (takes values 0..7) 0 / 0 \n" \ + "| (z=2...authenticated)\n" \ + "| rcode ...................... Response Code (0..15); interesting 0 / 0 \n" \ + "| values are:\n" \ + "| = 0 ...................... No Error Condition\n" \ + "| = 1 ...................... Unable to interprete query due to format error\n" \ + "| = 2 ...................... Unable to process due to server failure\n" \ + "| = 3 ...................... Name in query does not exist\n" \ + "| = 4 ...................... Type of query not supported\n" \ + "| = 5 ...................... Query refused\n" \ + "|\n" \ + "| Count values (values 0..65535) will be set automatically! You should not set these\n" \ + "| values manually except you are interested in invalid packets.\n" \ + "| qdcount (or qdc) ........... Number of entries in question section 1 / 1\n" \ + "| ancount (or anc) ........... Number of RRs in answer records section 0 / 1\n" \ + "| nscount (or nsc) ........... Number of name server RRs in authority 0 / 0\n" \ + "| records section\n" \ + "| arcount (or arc) ........... Number of RRs in additional records section 0 / 0\n" \ + "\n" + + +int dns_get_query (char* argval); +int dns_get_answer (char* argval); + + + +// Note: I do NOT use libnet here (had problems with bugs there...) +int create_dns_packet () +{ + + char *token, *tokenptr, argval[MAX_PAYLOAD_SIZE]; + + int i=0,j=0; + + unsigned char *x; + u_int16_t tmp; + + + // 16 bit values: + u_int8_t + dns_id0 =0, // DNS packet ID + dns_id1 =0, + dns_flags0 =0, // consists of the flags below + dns_flags1 =0, + dns_num_q0 =0, // number of questions + dns_num_q1 =0, + dns_num_ans0 =0, // number of answer resource records + dns_num_ans1 =0, + dns_num_aut0 =0, // number of authority resource records + dns_num_aut1 =0, + dns_num_add0 =0, // number of additional resource records + dns_num_add1 =0, + dns_type0 =0, + dns_type1 =0; + + + // bit fields for dns_flags1: Q/R(1), OPCODE(4), AA(1), TC(1), RD(1) + // bit fields for dns_flags0: RA(1), Z(3), RCODE(4) + u_int8_t + dns_flags_qr, // 1 bit + dns_flags_opcode, // 4 bits + dns_flags_aa, // 1 bit + dns_flags_tc, // 1 bit + dns_flags_rd, // 1 bit + // ---- next byte ----- + dns_flags_ra, // 1 bit + dns_flags_z, // 3 bits + dns_flags_rcode; // 4 bits + + + u_int8_t + dns_packet[MAX_PAYLOAD_SIZE], // finally the whole packet with all sections + section[MAX_PAYLOAD_SIZE]; // contains only a section (intermediately) + u_int32_t + dns_packet_s; + + + + if ( (getarg(tx.arg_string,"help", NULL)==1) && (mode==DNS) ) + { + if (mz_port) + { + cli_print(gcli, "%s", MZ_DNS_HELP); + return -1; + } + else + { + fprintf(stderr,"\n" + MAUSEZAHN_VERSION + "\n%s", MZ_DNS_HELP); + exit(0); + } + } + + + // general defaults: + // TODO: define globals in case dns is called by external functions!) + // MOST SAFEST AND EASIEST METHOD: define tx.dns_xxxx for default-initialization + // + dns_id0 = 0x42; // dns_id0 = (u_int8_t) ( ((float) rand()/RAND_MAX)*255); + dns_id1 = 0x42; + + dns_flags_qr = 0; // request + dns_flags_opcode = 0; // 'standard query' (also for response!) + + dns_type0 = 1; // A record + dns_type1 = 0; + + + i=0; + + + ///////////////////////////////////////////////////////////////////////////////// + // Evaluate CLI parameters: + + + // Handle the query // + + if ( (getarg(tx.arg_string,"query", argval)==1) || + (getarg(tx.arg_string,"q", argval)==1) ) + { + + (void) dns_get_query (argval); // returns the length in byte dns_num_q0=1; + + // copy the result from gbuf to our local buffer 'section': + for (j=0;j<gbuf_s;j++) + { + section[j]=gbuf[j]; + } + + i = gbuf_s; + + // Set defaults if not already set by callee. + // !! But ONLY set these if there is no additional answer section + // !! because then the answer section should set the defaults !!! + if ( (getarg(tx.arg_string,"answer", NULL)==0) && // no answer + (getarg(tx.arg_string,"a", NULL)==0) ) + { + if (!tx.dp) tx.dp = 53; + if (!tx.sp) tx.sp = 42000; + } + + + // These are the defaults for a query: + dns_flags_aa = 1; // authoritative answer + dns_flags_tc = 0; // not truncated + dns_flags_rd = 1; // recursion desired + dns_flags_ra = 0; // recursion available + dns_flags_z = 0; // FYI: if 010 = 2 = authenticated + dns_flags_rcode = 0; // no errors + dns_num_q0 = 1; // number of questions + } + + + + // Handle the answer: + // + // answer|a = <name>[:<type>[:<class>]]/[<ttl>:]<rdata>\n" + if ( (getarg(tx.arg_string,"answer", argval)==1) || + (getarg(tx.arg_string,"a", argval)==1) ) + { + + // In case there are multiple answer sections seperate them with / or | + token = strtok_r(argval,"/|",&tokenptr); + do + { + //then the corresponding answer section: + //first create a pointer to the <name>: + section[i]=0xc0; // a pointer always starts with MSB=11xxxxxx xxxxxxx = 0xc0 + i++; + section[i]=0x0c; // this number always points to the first query entry + i++; + //then add rdata + dns_num_ans0 += dns_get_answer (token); + //NOTE: 'i' points to the next free byte in section[] (see the query handling above) + for (j=0;j<gbuf_s;j++) + { + section[j+i]=gbuf[j]; + } + i=i+gbuf_s; // so 'i' again points to the next free byte in section[] + } while ( (token = strtok_r(NULL,"/|",&tokenptr))!=NULL); + + if (!tx.sp) tx.sp = 53; + if (!tx.dp) tx.dp = 42000; // should be set by user + dns_flags_qr = 1; // response + dns_flags_aa = 0; // no authoritative answer + dns_flags_tc = 0; // not truncated + dns_flags_rd = 1; // recursion desired + dns_flags_ra = 0; // recursion not available + dns_flags_z = 0; // FYI: if 010 = 2 = authenticated + dns_flags_rcode = 0; // no errors + } + + + + // *** NOTE *** + // Now 'i' contains the number of DNS payload bytes = valid bytes in section[] + // + + + /////////////////////////////////////////////////////////////////////////////////////////////// + // Now let's handle the optional other commands, if some user really changed them... + // + // + + if (getarg(tx.arg_string,"id",argval)==1) + { + tmp = (u_int16_t) str2int (argval); + x = (unsigned char*) &tmp; + + dns_id1 = *x; + x++; + dns_id0 = *x; + } + + + if ( (getarg(tx.arg_string,"opcode", argval)==1) || (getarg(tx.arg_string,"op", argval)==1)) + { + if (strncmp(argval,"std",3)==0) // standard query + { + dns_flags_opcode = 0; + } + else if (strncmp(argval,"inv",3)==0) // inverse query + { + dns_flags_opcode = 1; + } + else if (strncmp(argval,"sts",3)==0) // status server request + { + dns_flags_opcode = 2; + } + else // specified as integer + { + dns_flags_opcode = (u_int8_t) str2int (argval); + if (dns_flags_opcode > 15) + { + if (!quiet) + { + fprintf(stderr, "mz/dns: [Warning] Opcode cannot exceed 15 => will reduce to 15!\n"); + } + dns_flags_opcode = 15; + } + } + } + + + + + if (getarg(tx.arg_string,"aa",NULL)==1) + { + dns_flags_aa = 1; + } + + if (getarg(tx.arg_string,"!aa",NULL)==1) + { + dns_flags_aa = 0; + } + + if (getarg(tx.arg_string,"tc",NULL)==1) + { + dns_flags_tc = 1; + } + + if (getarg(tx.arg_string,"!tc",NULL)==1) + { + dns_flags_tc = 0; + } + + if (getarg(tx.arg_string,"rd",NULL)==1) + { + dns_flags_rd = 1; + } + + if (getarg(tx.arg_string,"!rd",NULL)==1) + { + dns_flags_rd = 0; + } + + if (getarg(tx.arg_string,"ra",NULL)==1) + { + dns_flags_ra = 1; + } + + if (getarg(tx.arg_string,"!ra",NULL)==1) + { + dns_flags_ra = 0; + } + + if (getarg(tx.arg_string,"z", argval)==1) + { + dns_flags_z = (u_int8_t) str2int (argval); + if (dns_flags_z > 7) + { + if (!quiet) + { + fprintf(stderr, "mz/dns: [Warning] z cannot exceed 7 => will reduce to 7!\n"); + } + dns_flags_z = 7; + } + } + + + + if (getarg(tx.arg_string,"rcode", argval)==1) + { + dns_flags_rcode = (u_int8_t) str2int (argval); + if (dns_flags_rcode > 15) + { + if (!quiet) + { + fprintf(stderr, "mz/dns: [Warning] rcode cannot exceed 15 => will reduce to 15!\n"); + } + dns_flags_rcode = 7; + } + } + + + if ( (getarg(tx.arg_string,"qdcount", argval)==1) || + (getarg(tx.arg_string,"qdc", argval)==1) || + (getarg(tx.arg_string,"qc", argval)==1) ) + + { + tmp = (u_int16_t) str2int (argval); + x = (unsigned char*) &tmp; + dns_num_q1 = *x; + x++; + dns_num_q0 = *x; + } + + if ( (getarg(tx.arg_string,"ancount", argval)==1) || + (getarg(tx.arg_string,"anc", argval)==1) ) + { + tmp = (u_int16_t) str2int (argval); + x = (unsigned char*) &tmp; + dns_num_ans1 = *x; + x++; + dns_num_ans0 = *x; + } + + if ( (getarg(tx.arg_string,"nscount", argval)==1) || + (getarg(tx.arg_string,"nsc", argval)==1) ) + { + tmp = (u_int16_t) str2int (argval); + x = (unsigned char*) &tmp; + dns_num_aut1 = *x; + x++; + dns_num_aut0 = *x; + } + + if ( (getarg(tx.arg_string,"arcount", argval)==1) || + (getarg(tx.arg_string,"arc", argval)==1) ) + { + tmp = (u_int16_t) str2int (argval); + x = (unsigned char*) &tmp; + dns_num_add1 = *x; + x++; + dns_num_add0 = *x; + } + + // + // End of optional parameter handling + // + /////////////////////////////////////////////////////////////////////////////////////////////// + + + + + ///////////////////////////////////////////////////////// + // Now put all together i. e. create the UDP payload + // + // bit fields for dns_flags1: Q/R(1), OPCODE(4), AA(1), TC(1), RD(1) + // bit fields for dns_flags0: RA(1), Z(3), RCODE(4) + // + // 7 6 5 4 3 2 1 0 + // +--+--+--+--+--+--+--+--+ + // |QR| OPCODE |AA|TC|RD| + // +--+--+--+--+--+--+--+--+ + // + // + // 7 6 5 4 3 2 1 0 + // +--+--+--+--+--+--+--+--+ + // |RA| Z | RCODE | + // +--+--+--+--+--+--+--+--+ + // + + //// Flags: MSB + dns_flags_qr <<= 7; + dns_flags1 |= dns_flags_qr; + + dns_flags_opcode <<= 3; + dns_flags1 |= dns_flags_opcode; + + dns_flags_aa <<= 2; + dns_flags1 |= dns_flags_aa; + + dns_flags_tc <<= 1; + dns_flags1 |= dns_flags_tc; + + dns_flags1 |= dns_flags_rd; + + //// Flags: LSB + + dns_flags_ra <<= 7; + dns_flags0 |= dns_flags_ra; + + dns_flags_z <<= 4; + dns_flags0 |= dns_flags_z; + + dns_flags0 |= dns_flags_rcode; + + //// Add header bytes to dns_packet: + + dns_packet[0]=dns_id1; + dns_packet[1]=dns_id0; + + dns_packet[2]=dns_flags1; + dns_packet[3]=dns_flags0; + + dns_packet[4]=dns_num_q1; + dns_packet[5]=dns_num_q0; + + dns_packet[6]=dns_num_ans1; + dns_packet[7]=dns_num_ans0; + + dns_packet[8]=dns_num_aut1; + dns_packet[9]=dns_num_aut0; + + dns_packet[10]=dns_num_add1; + dns_packet[11]=dns_num_add0; + + //// Add sections to dns_packet: + + + for (j=0; j<i; j++) + { + dns_packet[12+j] = section[j]; + } + + // + ////////////////////////////////////////////////////////// + + dns_packet_s = i+12; + tx.udp_payload_s = dns_packet_s; + + // copy the dns_paylod to the udp_payload + + for (j=0; j<tx.udp_payload_s; j++) + { + tx.udp_payload[j] = dns_packet[j]; + } + + tx.udp_len = 8 + tx.udp_payload_s; + + return dns_packet_s; +} + + + +//////////////////////////////////////////////////////////////////////////////////////////// +// Accepts a string like "www.perihel.at:A" or "www.perihel.at" +// and creates a valid query section using the global gbuf[] and gbuf_s +// +// query|q = <name>[:<type>]\n" +// Return value: +// number of queries (currently only 1 query accepted, +// hence return value is 1 on success or 0 upon failure +// +int dns_get_query(char* argval) +{ + char *token, *field, *saveptr1=NULL, *saveptr2=NULL; + int i,j, cnt; + u_int16_t tmp; + unsigned char *x; + + i=0; + + // now get first field: <name> + field = strtok_r(argval, ":", &saveptr1); + + // decompose <name> into labels: + token = strtok_r(field, ".", &saveptr2); + + do // loop through all labels + { + cnt = strlen(token); + gbuf[i] = cnt; + i++; + for (j=i; j<(i+cnt);j++) + { + gbuf[j] = *token; + token++; + } + i+=cnt; + + } while ( (token = strtok_r(NULL, ".", &saveptr2)) != NULL); + + gbuf[i]=0x00; + i++; // (always point to next empty byte) + + + // lets see if <type> has also been specified: + if ( (field = strtok_r(NULL, ":", &saveptr1)) !=NULL) + { + if ( (strncmp(field, "A",1)==0) || (strncmp(field, "a",1)==0) ) + { + tmp = 1; + } + else + { + tmp = (u_int16_t) str2int (field); + } + + x = (unsigned char*) &tmp; + + gbuf[i] = *(x+1); + i++; + gbuf[i] = *x; + i++; + } + else // use default type=A + { + gbuf[i] = 0x00; i++; + gbuf[i] = 0x01; i++; + } + + // finally add the class=IN: + gbuf[i] = 0x00; i++; + gbuf[i] = 0x01; i++; + + // this is the number of used bytes: + gbuf_s = i; + + //////// TEST + /* + for (j=0; j<i; j++) + { + printf("%02x \n",gbuf[j]); + } + printf("i=%u\n",i); + */ + + return 1; +} + + + + + + +// +// Given a label (e. g. www.google.com) creates correct bytes in *buf +// and returns number of bytes created. +// NOTE: Label MUST NOT be longer than 512 characters. +// +int dns_process_label(char* label, u_int8_t *buf) +{ + char *saveptr=NULL, *token; + int i=0, j=0, cnt=0, avoid_buffer_overflow=0; + + token = strtok_r(label, ".", &saveptr); + + do // loop through all labels + { + cnt = strlen(token); + i++; + *buf = cnt; + buf++; + avoid_buffer_overflow++; + for (j=0; j<cnt ;j++) + { + *buf = *token; + buf++; + avoid_buffer_overflow++; + if (avoid_buffer_overflow == 512) return 512; + token++; + } + i+=cnt; + + } while ( (token = strtok_r(NULL, ".", &saveptr)) != NULL); + *buf=0x00; + i++; // number of total bytes written + return i; +} + + + + + +// Accepts a valid triple of type:ttl:rdata and writes anything in gbuf[] and gbuf_s. +// +// Syntax examples: +// +// CNAME:3600:abc.com => Depending on type the rdata must be handled differently +// A:86400:192.168.1.33 => Up to 3 parameters +// A:192.168.1.33 => TTL may be omitted, then TTL=0 +// 192.168.1.44 => Single parameter can only be an A record +// +// Other TYPES than A and CNAME are currently not supported and therefore the user must +// specify RDATA in hex. +// + +int dns_get_answer(char* argval) +{ + char *field, *saveptr1=NULL; + char field1[512], field2[512], field3[512]; + int i, len, num_params; + u_int16_t TYPE=1; // A + u_int8_t *ptrTYPE; + u_int32_t TTL=0; + u_int8_t *ptrTTL; + u_int16_t RDLEN; + u_int8_t *ptrRDLEN; + u_int8_t rdata[512]; + + field1[0]='\0'; + field2[0]='\0'; + field3[0]='\0'; + + len = strlen (argval); + + // determine number of occurences of ':' + num_params=1; + for (i=0; i<len; i++) + { + if (argval[i]==':') num_params++; + } + if (num_params>3) return 0; // Error! + + // now get the fields (type, ttl, rdata) + field = strtok_r(argval, ":", &saveptr1); + strncpy(field1, field, 512); + if (num_params>1) // 2 or 3 + { + field = strtok_r(NULL, ":", &saveptr1); + strncpy(field2, field, 512); + if (num_params==3) + { + field = strtok_r(NULL, ":", &saveptr1); + strncpy(field3, field, 512); + } + } + + + // Now we have all parameters in field1, field2, and field3. + // But field2 and/or field3 might be empty. + + switch (num_params) + { + case 1: // only RDATA specified + strncpy(field3, field1, 512); + strcpy(field1, "A"); + strcpy(field2, "0"); + break; + case 2: // TYPE and RDATA + strncpy(field3, field2, 512); + strcpy(field2, "0"); + break; + } + + //CHECK: + //printf("fields: [%s] [%s] [%s]\n",field1,field2,field3); + + ////////////////////////////////////////////////////////////////////// + // Now create the whole answer section: Type, Class, TTL, RDLEN, RDATA + + //// TYPE + if ( (strcmp(field1,"CNAME")==0) || + (strcmp(field1,"cname")==0) ) + { + TYPE=5; + gbuf[0]=0x00; + gbuf[1]=0x05; + } + else if ( (strcmp(field1,"A")==0) || + (strcmp(field1,"a")==0) ) + { + TYPE=1; + gbuf[0]=0x00; + gbuf[1]=0x01; + } + else // type must be given as number + { + TYPE = (u_int16_t) str2int(field1); + ptrTYPE = (u_int8_t*) &TYPE; + gbuf[0]=*(ptrTYPE+1); + gbuf[1]=*(ptrTYPE); + } + + + //// CLASS = IN = 0x00 01 + gbuf[2]= 0x00; gbuf[3]=0x01; + + //// TTL + TTL = (u_int32_t) str2int(field2); + ptrTTL = (u_int8_t*) &TTL; + gbuf[4]= *(ptrTTL+3); + gbuf[5]= *(ptrTTL+2); + gbuf[6]= *(ptrTTL+1); + gbuf[7]= *(ptrTTL+0); + + + //// RDLEN and RDATA + if (TYPE==1) // A + { + RDLEN = num2hex(field3, rdata); // should be 4 if IP address + if (RDLEN!=4) + { + fprintf(stderr," mz/dns_get_answer: [WARNING] RDATA of A record should contain an IPv4 address (4 bytes).\n"); + } + } + else if (TYPE==5) // CNAME + { + RDLEN = dns_process_label (field3, rdata); + if (RDLEN==0) + { + fprintf(stderr," mz/dns_get_answer: [WARNING] RDATA must contain a domain name.\n"); + } + } + else // Any other type + { + RDLEN = str2hex(field3, rdata, 512); // should be 4 if IP address + } + + ptrRDLEN = (u_int8_t*) &RDLEN; + gbuf[8] = *(ptrRDLEN+1); + gbuf[9] = *(ptrRDLEN+0); + + + // finally write rdata + for (i=0; i<RDLEN; i++) + { + gbuf[10+i] = rdata[i]; + } + gbuf_s = 10+RDLEN; + + //////// TEST + /* + for (i=0; i<gbuf_s; i++) + { + printf("%02x \n",gbuf[i]); + } + printf("i=%u\n",i); + */ + + return 1; + +} diff --git a/staging/hextools.c b/staging/hextools.c new file mode 100644 index 0000000..0328600 --- /dev/null +++ b/staging/hextools.c @@ -0,0 +1,322 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008 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 + * +*/ + + + + + +/////////////////////////////////////////////////////////////////////////////////////////// +// +// Contains various tools for hex-based conversions and manipulation of bytestrings +// +// str2hex_mac ..... converts "00:01:02:0a:ff:ff" into u_int8_t dst[6] +// str2hex ..... converts "1a 00:00-2f" into u_int8_t dst[n] (any length) +// num2hex ..... converts "192.16.1.1" into u_int8_t dst[4] +// bs2str ..... converts {0,1,10} into "00-01-0A" +// getbytes ..... a stupid implementation of memcpy - prefer memcpy instead !!! +// str2ip32 ..... converts "192.168.0.1" into 3232235521 (u_int32_t) +// str2ip32_rev ..... same but assumes network byte order +// type2str ..... converts a u_int16_t into a string, e. g. 0x800 into "08:00" +// +//////////////////////////////////////////////////////////////////////////////////////////// + + +#include "mz.h" + + + +// converts MAC address specified in str into u_int8_t array +// Usage: str2hex_mac ( "00:01:02:aa:ff:ee", src_addr ) +// Returns 1 if specified MAC address string is invalid, 0 upon success. +int str2hex_mac(char* str, u_int8_t *addr) +{ + char *hs; + int i; + unsigned int test; + char tmp[32]; + + strcpy(tmp,str); // necessary because strtok cannot operate on fixed strings + + hs=(char*)strtok(tmp,"-:., "); + + for (i=0; i<6; i++) + { + test = (unsigned int) strtol (hs, NULL, 16); + if (test>0xff) return 1; + addr[i]=(u_int8_t) strtol (hs, NULL, 16); + hs = strtok(NULL,"-:., "); + if ( (hs == NULL ) && (i!=5) ) + { + // Not a valid MAC address + return 1; + } + } + + if (hs!=NULL) return 1; // more than 6 bytes + + return 0; +} + + + + +// Converts ascii hex values (string) into integer array +// For example "1a 00:00-2f" will be converted to {26, 0, 0, 47} +// +// NOTE: n ist the max number of bytes to be converted +// +// RETURN VALUE: number of bytes converted +// or -1 upon failure +// +int str2hex(char* str, u_int8_t *hp, int n) +{ + char *hs; + int curval,i; + + + if (strlen(str)==0) return 0; + + char tmp[8192]=""; //for very long payloads + + strncpy(tmp,str,8191); // necessary because strtok cannot operate on fixed strings + + hs=(char*)strtok(tmp,"-:., "); + + i=0; + do + { n--; + curval=strtol(hs,NULL,16); + if (curval>0xff) return -1; + hp[i]=(u_int8_t) curval; + i++; + } + while ((n) && ((hs=(char*)strtok(NULL,"-:., "))!= NULL)); + + return i; // return the length of the array +} + + + +// Converts ascii numbers (terminated string) into integer array +// Every byte can be specified as integers {0..255} +// For example "192.16.1.1" will be converted to {C0, 10, 01, 01} +// +// NOTE: Returns the number of converted bytes! +int num2hex(char* str, u_int8_t *hp) +{ + char *hs; + int i; + unsigned int curval; + + if (strlen(str)==0) return 0; + + char tmp[8192]=""; //for very long payloads + + strncpy(tmp,str,8192); // necessary because strtok cannot operate on fixed strings + + hs = (char*) strtok (tmp,"-:., "); + + i=0; + do + { + curval = (unsigned int) str2int(hs); + if (curval<256) + { + hp[i] = (u_int8_t) curval; + i++; + } + } + while ((hs=(char*)strtok(NULL,"-:., "))!= NULL); + //hp[i]='\0'; // termination not necessary + + return i; +} + + + +// Convert array of integers into string of hex +// E.g. {0,1,10} => "00-01-0A" +// Useful for verification messages. +int bs2str(u_int8_t *bs, char* str, int len) +{ + int i; + char t[4]; + + str[0]='\0'; + + for (i=0; i<len; i++) + { +// if (bs[i]<16) strcat(str,"0"); // enforce two hex digits (e.g. "0a") + + sprintf(t,"%02x:",bs[i]); + strcat(str,t); + } + str[strlen(str)-1]='\0'; //remove the last ":" + return 1; +} + + +// Extract contiguous sequence of bytes from an array +// NOTE: first element has number 1 !!! +// THIS IS DEPRECATED: PREFER memcpy INSTEAD !!! +int getbytes(u_int8_t *source, + u_int8_t *target, + int from, + int to) + +{ + int i; + + // Check wrong arguments + if (from<1) + { + return -1; + } + + // copy bytes + for (i=0; i<(to-from+1); i++) + { + target[i]=source[from-1+i]; + } + + return 1; +} + + +// Converts an IP address given in 'dotted decimal' into an unsigned 32-bit integer +// Example: "192.168.0.1" => 3232235521 +u_int32_t str2ip32 (char* str) +{ + u_int32_t ip = 0; + unsigned int a,b,c,d; + int r; + + // check whether str really contains an IP address + if (strlen(str)<3) return 0; + if (str==NULL) return 0; + + if ((r=sscanf(str,"%i.%i.%i.%i",&a,&b,&c,&d))==0) return 0; + if (r==EOF) return 0; + + /* or an alternative method... + // these are the four bytes of a dotted decimal notation IP address: + a = (unsigned int) strtol(strtok(str,"."), (char **)NULL, 10); + b = (unsigned int) strtol(strtok(NULL,"."), (char **)NULL, 10); + c = (unsigned int) strtol(strtok(NULL,"."), (char **)NULL, 10); + d = (unsigned int) strtol(strtok(NULL,"."), (char **)NULL, 10); + */ + + if ((a>255)||(b>255)||(c>255)||(d>255)) return 0; + + ip = d + 256*c + 256*256*b + 256*256*256*a; + + //check with: + //printf("str2ip32 got 4 bytes: %i %i %i %i\n",a,b,c,d); + //printf("str2ip32 returned %u\n",ip); + + return ip; +} + + +// Converts an IP address given in 'dotted decimal' into an unsigned 32-bit integer +// This version does the same as str2ip32() but in 'network byte order' +u_int32_t str2ip32_rev (char* str) +{ + u_int32_t ip = 0; + unsigned int a,b,c,d; + int r; + + // check whether str really contains an IP address + if (strlen(str)<3) return 0; + if (str==NULL) return 0; + + if ((r=sscanf(str,"%i.%i.%i.%i",&a,&b,&c,&d))==0) return 0; + if (r==EOF) return 0; + + /* or an alternative method... + // these are the four bytes of a dotted decimal notation IP address: + a = (unsigned int) strtol(strtok(str,"."), (char **)NULL, 10); + b = (unsigned int) strtol(strtok(NULL,"."), (char **)NULL, 10); + c = (unsigned int) strtol(strtok(NULL,"."), (char **)NULL, 10); + d = (unsigned int) strtol(strtok(NULL,"."), (char **)NULL, 10); + */ + + if ((a>255)||(b>255)||(c>255)||(d>255)) return 0; + + ip = a + b*256 + c*256*256 + d*256*256*256; + + //check with: + //printf("str2ip32 got 4 bytes: %i %i %i %i\n",a,b,c,d); + //printf("str2ip32 returned %u\n",ip); + + return ip; +} + + +// Converts a 2-byte value (e. g. a EtherType field) +// into a nice string using hex notation. +// Useful for verification messages. +// Example: type2str (tx.eth_type, msg) may result in msg="08:00" +// Return value: how many hex digits have been found. +int type2str(u_int16_t type, char *str) +{ + char hex[8]; + int i=0; + + (void) sprintf (hex, "%x",type); + i=strlen(hex); + + switch (i) + { + case 1: + str[0]='0'; + str[1]='0'; + str[2]=':'; + str[3]='0'; + str[4]=hex[0]; + str[5]='\0'; + break; + case 2: + str[0]='0'; + str[1]='0'; + str[2]=':'; + str[3]=hex[0]; + str[4]=hex[1]; + str[5]='\0'; + break; + case 3: + str[0]='0'; + str[1]=hex[0]; + str[2]=':'; + str[3]=hex[1]; + str[4]=hex[2]; + str[5]='\0'; + break; + case 4: + str[0]=hex[0]; + str[1]=hex[1]; + str[2]=':'; + str[3]=hex[2]; + str[4]=hex[3]; + str[5]='\0'; + break; + + } + return i; +} + diff --git a/staging/layer1.c b/staging/layer1.c new file mode 100644 index 0000000..f671bb5 --- /dev/null +++ b/staging/layer1.c @@ -0,0 +1,383 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008 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 + * +*/ + + + +// **************************************************************************** +// +// This section contains functions to send an arbitrary byte stream out of +// the network card. Currently it works perfect for Ethernet cards. +// +// TODO: Access to the 802.11 header +// +// **************************************************************************** + +#include "mz.h" +#include "cli.h" + +int send_eth() +{ + // Tasks: + // 1. Check 'eth_src_txt' and 'eth_dst_txt' which contain either a MAC address or a keyword + // 'eth_dst' can be set without having 'eth_src_txt' specified (the next 6 bytes of the + // 'arg_string' will be used). But if 'eth_src_txt' is given then also 'eth_dst_txt' + // should have been specified, otherwise a default (ff-ff-ff-ff-ff-ff) will be used. + // 2. Check whether 'arg_string' contains a hex-string. If YES then convert it into an + // 'eth_payload' and extract eth_type. + // 3. Apply 'padding' if specified + // 4. Check if frame has at least minimum length (14 Bytes). + // 5. Send frame 'count' times and + // 6. Apply 'delay' (make precautions for better delay functions) + + int + src, // flag telling whether user has specified a source address + dst, // flag telling whether user has specified a destination address + src_random=0, + dst_random=0, + byte_ptr=1, + bytestring_s=0, + min_size=15, + pad=0, + repeat, loop, update, + i=0, + j=0; + char + err_buf[LIBNET_ERRBUF_SIZE], + message[MAX_PAYLOAD_SIZE*3]; + + u_int8_t bytestring[MAX_PAYLOAD_SIZE]; + libnet_ptag_t t; + libnet_t *l; + + + + if (tx.dot1Q) + { + fprintf(stderr," Note: raw layer 2 mode does not support 802.1Q builder.\n" + " If you want to create VLAN tags then you must do it by hand.\n"); + exit(1); + } + + if (tx.mpls) + { + fprintf(stderr," Note: raw layer 2 mode does not support MPLS builder.\n" + " If you want to create MPLS labels then you must do it by hand.\n"); + exit(1); + } + + + + + // So other functions can use this function for sending Ethernet frames + // These other functions must set dst, src, type and payload! + if (tx.eth_params_already_set) goto ALL_SPECIFIED; + + + if ((tx.padding) && (tx.padding<15)) // Note: ignored if padding==0 + { + tx.padding=15; + if (mz_port) { + cli_print(gcli, "Note: Set padding to 15 bytes (total length)\n"); + } else + fprintf(stderr, " mz/send_eth: [Note] adjusted minimum frame size to 15 bytes.\n"); + } + + // Create a temporal, local bytestring: + // + for (i=0; i<MAX_PAYLOAD_SIZE; i++) bytestring[i]=0x00; + bytestring_s = str2hex (tx.arg_string, bytestring, MAX_PAYLOAD_SIZE); + + // Set the flags to shorten subsequent decisions: + src = strlen(tx.eth_src_txt); + dst = strlen(tx.eth_dst_txt); + + + // IN ANY CASE if src has been specified: + // + if (src) + { + // Evaluate Ethernet CLI options (-a and -b) + if (check_eth_mac_txt(ETH_SRC)) // if true then problem! + { + // use own (already set in init.c) + } + src_random = tx.eth_src_rand; // local vars are faster + } + + // IN ANY CASE if dst has been specified: + // + if (dst) + { + // Evaluate Ethernet CLI options (-a and -b) + if (check_eth_mac_txt(ETH_DST)) // if true then problem! + { + str2hex("ff:ff:ff:ff:ff:ff",tx.eth_dst, 6); // the default + } + dst_random = tx.eth_dst_rand; // local vars are faster + } + + + // Catch errors with too short bytestrings: + // + if (src) + { + // bytestring only needs to contain eth_type + min_size-=12; + } + else if (dst) + { + // bytstring must contain src and type + min_size-=6; + } + if (bytestring_s < min_size) + { + j = min_size - bytestring_s; // this is what's missing + bytestring_s += j; // note that bytestring has been filled up with 0x00, so we can do that + } + + + // ADDENDUM: If src specified, dst missing: + // + if ( (!dst) && (src) ) + { + str2hex_mac ("ff:ff:ff:ff:ff:ff", tx.eth_dst); + } + + + + // ADDENDUM: If dst specified, src missing: + // + if ((dst) && (!src)) + { + // Get eth_src from bytestring: + if (bytestring_s>=6) { + (void) getbytes (bytestring, tx.eth_src, byte_ptr, byte_ptr+5); + byte_ptr=7; // now points to eth_type within bytestring + } + } + + // FINALLY: If both dst and src have NOT been specified: + // + if ((!dst) && (!src)) + { + if (bytestring_s>=6) { + (void) getbytes (bytestring, tx.eth_dst, byte_ptr, byte_ptr+5); + byte_ptr=7; + } + + if (bytestring_s>=12) { + (void) getbytes (bytestring, tx.eth_src, byte_ptr, byte_ptr+5); + byte_ptr=13; // points to eth_type + } + } + + // Set eth_type: + // + if (bytestring_s>=2) { + tx.eth_type = 256 * bytestring[byte_ptr-1] + bytestring[byte_ptr]; // don't forget: byte_ptr counts from 1 not 0 + byte_ptr+=2; // points to first payload byte (if available) + } + + + // Get remaining payload: + // + if ( (tx.eth_payload_s = bytestring_s - byte_ptr +1) > 0 ) // if there are any remaining bytes + { + (void) getbytes (bytestring, tx.eth_payload, byte_ptr, bytestring_s); + } + + + + // Add padding if desired. + // Note: padding means 'extend to given length' (not 'add these number of bytes') + if (tx.padding) + { + pad = tx.padding - (14 + tx.eth_payload_s); // number of additonal pad bytes required + for (i=0; i<pad; i++) + { + // tx.eth_payload[i+tx.eth_payload_s] = (u_int8_t) ( ((float) rand()/RAND_MAX)*256); + tx.eth_payload[i+tx.eth_payload_s] = 0x00; + } + tx.eth_payload_s += pad; + } + + + + ALL_SPECIFIED: + // *** All Ethernet parameters have been determined ! + // *** Now let's send the frame! + + l = libnet_init (LIBNET_LINK_ADV, tx.device, err_buf ); + + if (l == NULL) + { + fprintf(stderr, " mz/send_eth: libnet_init() failed (%s)", err_buf); + return -1; + } + + repeat=1; + + if (tx.count == 0) + { + loop = 1000000; + if (!quiet) + fprintf(stderr, " mz: !!! Infinite mode! Will send frames until you stop Mausezahn!!!\n"); + } + else + loop = tx.count; + + if ( (!quiet) && (!tx.delay) && (tx.count==0) ) + fprintf(stderr, " mz: !!! Will send at maximum frame rate without feedback!!!\n"); + + t=0; + update=1; + + // this is for the statistics: + mz_start = clock(); + total_d = tx.count; + + while (repeat) + { + if (tx.count!=0) repeat=0; // count=0 means repeat ad inifinitum + + for (i=0; i<loop; i++) + { + if (src_random) + { + tx.eth_src[0] = (u_int8_t) ( ((float) rand()/RAND_MAX)*256) & 0xFE; + tx.eth_src[1] = (u_int8_t) ( ((float) rand()/RAND_MAX)*256); + tx.eth_src[2] = (u_int8_t) ( ((float) rand()/RAND_MAX)*256); + tx.eth_src[3] = (u_int8_t) ( ((float) rand()/RAND_MAX)*256); + tx.eth_src[4] = (u_int8_t) ( ((float) rand()/RAND_MAX)*256); + tx.eth_src[5] = (u_int8_t) ( ((float) rand()/RAND_MAX)*256); + update=1; + } + + if (dst_random) + { + tx.eth_dst[0] = (u_int8_t) ( ((float) rand()/RAND_MAX)*256); + tx.eth_dst[1] = (u_int8_t) ( ((float) rand()/RAND_MAX)*256); + tx.eth_dst[2] = (u_int8_t) ( ((float) rand()/RAND_MAX)*256); + tx.eth_dst[3] = (u_int8_t) ( ((float) rand()/RAND_MAX)*256); + tx.eth_dst[4] = (u_int8_t) ( ((float) rand()/RAND_MAX)*256); + tx.eth_dst[5] = (u_int8_t) ( ((float) rand()/RAND_MAX)*256); + update=1; + } + + if (update) // new frame parameters + { + t = libnet_build_ethernet (tx.eth_dst, + tx.eth_src, + tx.eth_type, + tx.eth_payload, + tx.eth_payload_s, + l, + t); + + if (t == -1) + { + fprintf(stderr, " mz/send_eth: %s", libnet_geterror(l)); + return -1; + } + + if (verbose) + { + bs2str (tx.eth_dst, message, 6); // DA + fprintf(stderr, " mz: send %s",message); + + bs2str (tx.eth_src, message, 6); // SA + fprintf(stderr, " %s",message); + + type2str(tx.eth_type, message); + fprintf(stderr, " %s",message); // Type + + bs2str (tx.eth_payload, message, tx.eth_payload_s); // Payload + fprintf(stderr, " %s\n",message); + } + update=0; + if (verbose==2) + { + fprintf(stderr, "\n"); + fprintf(stderr, "*** NOTE: Simulation only! Nothing has been sent! ***\n"); + libnet_destroy(l); + return 0; + } + + + } + + libnet_write(l); + + if (tx.delay) + { + SLEEP (tx.delay); + if ( (verbose) && (!src_random) && (!dst_random) ) + { + fprintf(stderr, "."); + } + } + + } // end for + + } // end while + + if (verbose) + { + if ((tx.delay) || (tx.count==0)) + { + fprintf(stderr,"\n"); + } + + fprintf(stderr, " mz: sent %u frames.\n",loop); + } + + + + libnet_destroy(l); + + + return 0; +} + + + +// ========================================================================================== + + /* + if (verbose) + { + fprintf(stderr," mz/send_bytes: \n"); + bs2str(da,dast,6); + fprintf(stderr," DA = %s", dast); + bs2str(sa,sast,6); + fprintf(stderr," SA = %s", sast); + fprintf(stderr," type = %x",et); + bs2str(payload,pl,payload_s); + fprintf(stderr," data = %s\n",pl); + } + */ + + + + + + + + + + diff --git a/staging/layer2.c b/staging/layer2.c new file mode 100644 index 0000000..ebbc7d8 --- /dev/null +++ b/staging/layer2.c @@ -0,0 +1,902 @@ +/* + * 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 + * +*/ + + + +// *************************************************************************** +// This sections contains functions to send various L2-based PDUs such as +// * ARP +// * BPDU +// *************************************************************************** + +#include "mz.h" +#include "cli.h" + + + +#define MZ_ARP_HELP \ + "| ARP type: Send arbitrary ARP packets.\n" \ + "| Note:\n" \ + "| - The Ethernet dst and src MAC addresses can be specified but can be also 'rand'.\n" \ + "| - If dst and src are NOT specified then practical defaults are used (src=own MAC, dst=bc).\n" \ + "|\n" \ + "| ARGUMENT SYNTAX: <command> [<parameters>]\n" \ + "| | |\n" \ + "| help, request, reply --+ |\n" \ + "| +-- sendermac, senderip, targetmac, targetip\n" \ + "| smac sip tmac tip\n" \ + "|\n" \ + "| EXAMPLES:\n" \ + "| 1. Legitimate ARP response to broadcast:\n" \ + "| # mz eth0 -t arp \"reply\"\n" \ + "| 2. ARP cache poisoning, claiming to be 192.168.0.1, telling a target PC:\n" \ + "| # mz eth0 -t arp \"reply, senderip=192.168.0.1, targetmac=00:00:0c:01:02:03, targetip=172.16.1.50\"\n" \ + "\n" + + +#define MZ_BPDU_HELP \ + "| BPDU type: Send arbitrary BPDU packets (spanning tree).\n" \ + "|\n" \ + "| ARGUMENT SYNTAX: <command> [<parameters>]\n" \ + "| | \n" \ + "| conf, tcn --+ \n" \ + "| \n" \ + "| Parameters:\n" \ + "|\n" \ + "| id = 0-65535 ..... default: 0, identifies 'Spanning Tree Protocol'\n" \ + "| version = 0-255 ..... default: 0\n" \ + "| type = 0-255 ..... BPDU Type: 0=CONF, 1=TCN (default: CONF)\n" \ + "| flags = 0-255 ..... 1=TC, 128=ACK (default: 0 = No TC, No ACK)\n" \ + "| rootid = <pri>:<mac> ..... 8 byte Root-ID (default: 00:00:<own-mac>)\n" \ + "| rootpc = 0-4294967295 ..... root path cost (default: 0)\n" \ + "| bid = <mac> ..... 6 byte MAC address (default: own-mac)\n" \ + "| pid = 0-65535 ..... port identifier (default: 0)\n" \ + "| age = 0-65535 ..... message age (default: 0)\n" \ + "| maxage = 0-65535 ..... max age (default: 20)\n" \ + "| hello = 0-65535 ..... hello time (default: 2)\n" \ + "| fwd = 0-65535 ..... forward delay (default: 15)\n" \ + "| tag - ..... Keyword to enforce 802.1Q VLAN tag; use this\n" \ + "| together with the 'vlan' parameter below.\n" \ + "|\n" \ + "| PVST+ extensions:\n" \ + "|\n" \ + "| vlan ..... VLAN number (default: 0)\n" \ + "| pri ..... 802.1P-Priority (0-7, default: 0)\n" \ + "| notag ..... Omit 802.1Q VLAN tag\n" \ + "| \n" \ + "|\n" \ + "| DEFAULTS: mz sends standard IEEE 802.1d (CST) BPDUs and assumes that your computer\n" \ + "| wants to become the root bridge (rid=bid). Configuration BPDUs are the default but\n" \ + "| can be changed using the 'tcn' keyword. Optionally the 802.3 source and destination\n" \ + "| MAC addresses can be specified using the -a and -b options. Per default, the correct\n" \ + "| STP or PVST+ destination addresses are used (same as '-b stp' or '-b pvst', \n" \ + "| respectively).\n" \ + "| \n" \ + "| Note that the parameter 'vlan' only selects the PVST+ mode if the parameter 'tag' is\n" \ + "| NOT used.\n" \ + "\n" + + + +// Send arbitrary ARP packets. +// Note: +// - The Ethernet dst and src MAC addresses can be specified, +// the eth_src_txt can be 'rand' +// - If eth_dst and eth_src are NOT specified then practical defaults are used +// +// arg_string syntax: <command>, <param>, ... , <param> +// - commands: 'request' OR 'reply' +// - params: 'sendermac', 'senderip', 'targetmac', 'targetip' +// +// Example arg_string for ARP cache poisoning: +// "reply, senderip=192.168.0.1, targetmac=00:00:0c:01:02:03, targetip=172.16.1.50" +// where sendermac will be automatically replaced by own mac, +// senderip is the spoofed IP, +// targetmac and targetip identifies the receiver. +// +int send_arp () +{ + libnet_t *l; + libnet_ptag_t t; + + char + argval[64], + t1[64], + t2[64], + src, + dst, + errbuf[LIBNET_ERRBUF_SIZE]; + + int + i, + arpmode=0, + arpop=0, + loop, + tm=0; + + u_int8_t + *packet, + sendermac[6], + targetmac[6]; + + + + u_int32_t + packet_s, + senderip=0, + targetip=0; + + + if (tx.dot1Q) + { + fprintf(stderr," Note: ARP mode does not support 802.1Q builder.\n"); + exit(1); + } + + if (tx.mpls) + { + fprintf(stderr," Note: ARP mode does not support MPLS builder.\n"); + exit(1); + } + + if (getarg(tx.arg_string,"help", NULL)==1) + { + if (mz_port) + { + cli_print(gcli, "%s", MZ_ARP_HELP); + return -1; + } + else + { + fprintf(stderr,"\n" + MAUSEZAHN_VERSION + "\n%s", MZ_ARP_HELP); + exit(0); + } + } + + + // Set the flags to shorten subsequent decisions: + src = strlen(tx.eth_src_txt); + dst = strlen(tx.eth_dst_txt); + + l = libnet_init(LIBNET_LINK_ADV, tx.device, errbuf); + + if (l == NULL) + { + fprintf(stderr, "%s", errbuf); + exit(EXIT_FAILURE); + } + + + + if (getarg(tx.arg_string,"request", NULL)==1) + { + arpmode=1; + arpop = ARPOP_REQUEST; + } + else + if (getarg(tx.arg_string, "reply", NULL)==1) + { + arpmode=2; + arpop = ARPOP_REPLY; + } + else + { // Default: + arpmode=2; + arpop = ARPOP_REPLY; + } + + + + if ( (getarg(tx.arg_string,"sendermac", argval)==1) || (getarg(tx.arg_string,"smac", argval)==1) ) + { + //TODO: Allow 'rand' as sendermac + str2hex(argval,sendermac,6); + } + else + { + // sendermac is usually ALWAYS own MAC: + getbytes(tx.eth_src, sendermac,1,6); + } + + + if ( (getarg(tx.arg_string,"targetmac", argval)==1) || (getarg(tx.arg_string,"tmac", argval)==1) ) + { + str2hex(argval,targetmac,6); + tm=1; + } + else + { + // targetmac is either zero (request) or bcast (reply=>gratitious ARP) + if (arpmode==1) //request + str2hex("00:00:00:00:00:00",targetmac, 6); + else //reply + str2hex("ff:ff:ff:ff:ff:ff",targetmac, 6); + } + + + if ( (getarg(tx.arg_string,"senderip", argval)==1) || (getarg(tx.arg_string,"sip", argval)==1) ) + { + senderip = str2ip32_rev(argval); + } + else + { + // senderip is usually ALWAYS the own IP + senderip = libnet_get_ipaddr4(l); // TODO - use tx.ip_src + } + + + + if ( (getarg(tx.arg_string,"targetip", argval)==1) || (getarg(tx.arg_string,"tip", argval)==1) ) + { + targetip = str2ip32_rev(argval); + } + else + { + // if targetip is missing also use own IP because it may be used for duplicate IP detection + targetip = libnet_get_ipaddr4(l); + } + + + + // NOTE: Now all ARP parameters are set (possibly defaults used!) + + bs2str(sendermac,t1,6); + bs2str(targetmac,t2,6); + //Check: + //printf("-- sendermac=%s targetmac=%s senderip=%u targetip=%u\n",t1,t2,senderip,targetip); + + + + // Build the ARP header + + t = libnet_autobuild_arp(arpop, /* operation type */ + sendermac, /* sender hardware addr */ + (u_int8_t *)&senderip, /* sender protocol addr */ + targetmac, /* target hardware addr */ + (u_int8_t *)&targetip, /* target protocol addr */ + l); /* libnet context */ + + if (t == -1) + { + fprintf(stderr, " mz/send_arp: Can't build ARP header: %s\n", libnet_geterror(l)); + exit(EXIT_FAILURE); + } + + + // Finally build the Ethernet header + + if ((!dst) && (!src)) // ... user does not care about addresses (both eth_dst and eth_src NOT specified) + { + if (arpmode==1) + str2hex("ff:ff:ff:ff:ff:ff", tx.eth_dst, 6); + else + getbytes(targetmac, tx.eth_dst, 1, 6); // either also bcast or specific MAC + + t = libnet_autobuild_ethernet(tx.eth_dst, /* ethernet destination */ + ETHERTYPE_ARP, /* protocol type */ + l); /* libnet handle */ + + if (t == -1) + { + fprintf(stderr, " mz/send_arp: Can't build ethernet header: %s\n", + libnet_geterror(l)); + exit(EXIT_FAILURE); + } + } + else // EITHER eth_dst OR eth_src OR BOTH specified: + { + if (!dst) + { + if (arpmode==1) + str2hex("ff:ff:ff:ff:ff:ff", tx.eth_dst, 6); + else + getbytes(targetmac, tx.eth_dst, 1, 6); // either also bcast when reply or specific MAC + } + else // eth_dst specified + { + if (check_eth_mac_txt(ETH_DST)) // if true then problem! + { + str2hex("ff:ff:ff:ff:ff:ff",tx.eth_dst, 6); // the default + } + } + + + if (!src) + { + // tx.eth_src contains own MAC by default! + } + else // use specified source MAC address + { + if (check_eth_mac_txt(ETH_SRC)) // if true then problem! + { + str2hex("ff:ff:ff:ff:ff:ff",tx.eth_src, 6); // the default + } + } + + t = libnet_build_ethernet (tx.eth_dst, tx.eth_src, ETHERTYPE_ARP, NULL, 0, l, 0); // Note: payload=NULL, payload_s=0 + } + + if (libnet_adv_cull_packet(l, &packet, &packet_s) == -1) + { + fprintf(stderr, "%s", libnet_geterror(l)); + } + else + { + libnet_adv_free_packet(l, packet); + } + + // this is for the statistics: + mz_start = clock(); + total_d = tx.count; + + + again: + + if (tx.count==0) + loop=1000000; + else + loop=tx.count; + + for (i=1; i<=loop; i++) + { + + if (!simulate) libnet_write(l); + + if (verbose) + { + fprintf(stderr," sent ARP: %s smac=%s sip=%s tmac=%s tip=%s\n", + (arpmode==1) ? "request" : "reply", + t1, + libnet_addr2name4(senderip,LIBNET_DONT_RESOLVE), + t2, + libnet_addr2name4(targetip,LIBNET_DONT_RESOLVE)); + } + + + if (tx.delay) SLEEP (tx.delay); + } + + if (tx.count==0) + { + goto again; + } + + + libnet_destroy(l); + + return 0; +} + + + + + + + + + + + + + + + +/////////////////////////////////////////////////////////////////////////////////////// +// Send arbitrary BPDU frames. +// +// commands: +// conf|tcn ...when specifying everything yourself +// +// params: +// id, version, type, flags, rootid, rootpc, bid, pid, age, maxage, hello, fwd, +// vlan +// +// defaults: +// mz assumes you want to become root bridge! (rid=bid) +// +int send_bpdu () +{ + + // BPDU parameters: + u_int16_t + id=0; + u_int8_t + version=0, + bpdu_type=0, // 0=conf, 1=topology change (actually in big endian!) + flags=0, // 1=TC, 128=TCAck + root_id[8], // Root BID + bridge_id[8]; // Own BID + u_int32_t + root_pc=0; // Root Path Cost + u_int16_t + port_id=0, // Port Identifier + message_age=0, // All timers are multiples of 1/256 sec. Thus times range from 0 to 256 seconds. + max_age=20, + hello_time=2, // + f_delay=15; + + // LLC Parameters: + u_int8_t + dsap=0x42, + ssap=0x42, + control=0x3; + + // Optional payload (needed for PVST+) + u_int8_t + bpdu_payload[64], + snap_oui[3]; + u_int32_t + bpdu_payload_s=0; + u_int16_t + vlan=0; + u_int8_t + priority=0x00, + *x; + int + tag=0; + + + // Standard libnet variables: + libnet_t *l; + libnet_ptag_t t; + char errbuf[LIBNET_ERRBUF_SIZE]; + + // Other variables: + unsigned int i, loop; + int bpdumode=0; + char argval[64]; + char dum1[32], dum2[32]; + + + if (tx.dot1Q) + { + fprintf(stderr," Note: BPDU mode does not support 802.1Q builder.\n"); + exit(1); + } + + if (tx.mpls) + { + fprintf(stderr," Note: BPDU mode does not support MPLS builder.\n"); + exit(1); + } + + + // HELP TEXT + if (getarg(tx.arg_string,"help", NULL)==1) + { + if (mz_port) + { + cli_print(gcli, "%s", MZ_BPDU_HELP); + return -1; + } + else + { + fprintf(stderr,"\n" + MAUSEZAHN_VERSION + "\n%s", MZ_BPDU_HELP); + exit(0); + } + } + + ///////////////////////////////////////////////////////// + // Default Destination Address + if (check_eth_mac_txt(ETH_DST)) // if true then problem! + { + str2hex("01:80:C2:00:00:00",tx.eth_dst, 6); // if '1' then user did not set MAC address (or problem occurred) + } + + // Default Bridge-ID + bridge_id[0]=0x00; + bridge_id[1]=0x00; + for (i=0; i<6; i++) bridge_id[2+i]=tx.eth_src[i]; + for (i=0; i<8; i++) root_id[i]=bridge_id[i]; + ///////////////////////////////////////////////////////// + + + + + // determine BPDU type: + if (getarg(tx.arg_string,"conf", NULL)==1) + { + bpdumode=1; + tx.eth_len = LIBNET_802_2_H + LIBNET_STP_CONF_H; + } + else + if (getarg(tx.arg_string, "tcn", NULL)==1) + { + bpdumode=2; + tx.eth_len = LIBNET_802_2_H + LIBNET_STP_TCN_H; + bpdu_type=0x80; + } + else // default + { + bpdumode=1; + tx.eth_len = LIBNET_802_2_H + LIBNET_STP_CONF_H; + } + + +// Commands summary: +// id, version, type, flags, rid, rootpc, bid, pid, age, maxage, hello, fwd + + if (getarg(tx.arg_string,"id", argval)==1) + { + id = (u_int16_t) str2int(argval); + } + + if (getarg(tx.arg_string,"version", argval)==1) + { + version = (u_int8_t) str2int(argval); + } + + if (getarg(tx.arg_string,"bpdu_type", argval)==1) + { + bpdu_type = (u_int8_t) str2int(argval); + } + + if (getarg(tx.arg_string,"flags", argval)==1) + { + flags = (u_int8_t) str2int(argval); + } + + if (getarg(tx.arg_string,"rid", argval)==1) + { + if (str2hex(argval,root_id, 8)!=8) + { + fprintf(stderr," mz/send_bpdu: [ERROR] The root-id must be exactly 8 bytes!\n"); + exit (-1); + } + } + + if (getarg(tx.arg_string,"rootpc", argval)==1) + { + root_pc = (u_int32_t) str2int(argval); + } + + if (getarg(tx.arg_string,"bid", argval)==1) + { + if (str2hex(argval,bridge_id, 6)!=6) + { + fprintf(stderr," mz/send_bpdu: [ERROR] The bridge-id must be exactly 6 bytes!\n"); + exit (-1); + } + } + + if (getarg(tx.arg_string,"pid", argval)==1) + { + port_id = (u_int16_t) str2int(argval); + } + + if (getarg(tx.arg_string,"age", argval)==1) + { + message_age = (u_int16_t) str2int(argval); + } + + if (getarg(tx.arg_string,"maxage", argval)==1) + { + max_age = (u_int16_t) str2int(argval); + } + + if (getarg(tx.arg_string,"hello", argval)==1) + { + hello_time = (u_int16_t) str2int(argval); + } + + if (getarg(tx.arg_string,"fwd", argval)==1) + { + f_delay = (u_int16_t) str2int(argval); + } + + + + if (getarg(tx.arg_string,"vlan", argval)==1) + { + // PVST+ uses TLVs of type=0x00, len=0x02, and Value=0xVV which is the VLAN ID + // The DA must be 0100.0ccc.cccd instead of the standard 0180.c200.0000 + // + if (check_eth_mac_txt(ETH_DST)) // if '1' then user did not set MAC address (or problem occurred) + { + str2hex("01:00:0C:CC:CC:CD",tx.eth_dst, 6); // Cisco PVST+ address + } + +/* // OLD TLV, maybe wrong, maybe obsolete, I don't know. + + bpdu_payload[0] = 0x34; + bpdu_payload[1] = 0x00; + bpdu_payload[2] = 0x02; + vlan = (u_int16_t) str2int(argval); + + x = (u_int8_t*) &vlan; + bpdu_payload[3] = *(x+1); + bpdu_payload[4] = *(x); + bpdu_payload[5] = 0x00; + bpdu_payload[6] = 0x00; + bpdu_payload_s = 7; +*/ + // Updated PVST+ TLV: + bpdu_payload[0] = 0x00; + bpdu_payload[1] = 0x00; + bpdu_payload[2] = 0x00; + bpdu_payload[3] = 0x00; + bpdu_payload[4] = 0x02; + vlan = (u_int16_t) str2int(argval); + x = (u_int8_t*) &vlan; + bpdu_payload[5] = *(x+1); + bpdu_payload[6] = *(x); + bpdu_payload_s = 7; + + tag=1; // set the default: Use 802.1Q tag !!! + } + else // even a normal BPDU must be padded to 60 bytes (total) + { + bpdu_payload[0] = 0x00; + bpdu_payload[1] = 0x00; + bpdu_payload[2] = 0x00; + bpdu_payload[3] = 0x00; + bpdu_payload[4] = 0x00; + bpdu_payload[5] = 0x00; + bpdu_payload[6] = 0x00; + bpdu_payload[7] = 0x00; + bpdu_payload_s = 8; + + tag=0; // set the default: send untagged !!! + } + + + // Note: The order is important because above the defaults for 'tag' has been set. + // + if (getarg(tx.arg_string,"notag", NULL)==1) + { + tag=0; + } + + + // Send normal BPDU with VLAN tag + if (getarg(tx.arg_string,"tag", NULL)==1) + { + tag=2; + bpdu_payload[0] = 0x00; + bpdu_payload[1] = 0x00; + bpdu_payload[2] = 0x00; + bpdu_payload[3] = 0x00; + bpdu_payload[4] = 0x00; + bpdu_payload[5] = 0x00; + bpdu_payload[6] = 0x00; + bpdu_payload[7] = 0x00; + bpdu_payload_s = 8; + + // Rewrite to standard 0180.c200.0000 + // + if (check_eth_mac_txt(ETH_DST)) // if '1' then user did not set MAC address (or problem occurred) + { + str2hex("01:80:C2:00:00:00",tx.eth_dst, 6); + } + vlan = (u_int16_t) str2int(argval); + } + + + if (getarg(tx.arg_string,"pri", argval)==1) + { + priority = (u_int8_t) str2int(argval); + if (priority>7) + { + fprintf(stderr, " mz/send_bpdu: Priority must be between 0 and 7.\n"); + exit(1); + } + + if (tag==0) + { + fprintf(stderr, " mz/send_bpdu: Priority cannot be used together with the 'notag' keyword.\n"); + exit(1); + } + } + + + // Open the link - get libnet handle + l = libnet_init(LIBNET_LINK_ADV, tx.device, errbuf); + + if (l == NULL) + { + fprintf(stderr, "%s", errbuf); + exit(EXIT_FAILURE); + } + + + if (bpdumode==1) // Prepare CONFIGURATION BPDU: + { + + t = libnet_build_stp_conf (id, + version, + bpdu_type, + flags, + root_id, + root_pc, + bridge_id, + port_id, + message_age, + max_age, + hello_time, + f_delay, + (bpdu_payload_s) ? bpdu_payload : NULL, + bpdu_payload_s, + l, + 0); + + if (t == -1) + { + fprintf(stderr, " mz/send_bpdu: Can't build BPDU header: %s\n", + libnet_geterror(l)); + exit(EXIT_FAILURE); + } + } + else // Topology Change BPDU + { + t = libnet_build_stp_tcn(id, + version, + bpdu_type, + (bpdu_payload_s) ? bpdu_payload : NULL, + bpdu_payload_s, + l, + 0); + if (t == -1) + { + + fprintf(stderr, " mz/send_bpdu: Can't build BPDU header: %s\n", + libnet_geterror(l)); + exit(EXIT_FAILURE); + } + } + + + + if ( (vlan==0) || (tag==2) ) // normal BPDU + { + // normal LLC without SNAP + t = libnet_build_802_2 (dsap, + ssap, + control, + NULL, + 0, + l, + 0); + + if (t == -1) + { + fprintf(stderr, " mz/send_bpdu: Can't build LLC header: %s\n", + libnet_geterror(l)); + exit(EXIT_FAILURE); + } + } + else // PVST+ => LLC with SNAP + { + snap_oui[0]=0x00; + snap_oui[1]=0x00; + snap_oui[2]=0x0c; + + // requires a SNAP header with oui=0x00000c and type=0x010b + t = libnet_build_802_2snap(0xAA, + 0xAA, + 0x03, + snap_oui, + 0x010b, + NULL, + 0, + l, + 0); + + if (t == -1) + { + fprintf(stderr, " mz/send_bpdu: Can't build SNAP header: %s\n", + libnet_geterror(l)); + exit(EXIT_FAILURE); + } + } + + + if (tag==0) + { + // Normal 802.3 header without VLAN tag + t = libnet_build_802_3 (tx.eth_dst, + tx.eth_src, + (vlan) ? 0x36 : tx.eth_len, // NOTE the LENGTH field => 802.3 header! + NULL, + 0, + l, + 0); + + } + else // PVST+ => 802.3 with 802.1Q + { + t = libnet_build_802_1q(tx.eth_dst, + tx.eth_src, + 0x8100, + priority, + 0x00, // CFI + vlan, + 0x32, //tx.eth_len, + NULL, + 0, + l, + 0); + } + + + if (t == -1) + { + fprintf(stderr, " mz/send_bpdu: Can't build 802.3 header: %s\n", + libnet_geterror(l)); + exit(EXIT_FAILURE); + } + + + // This is ugly but it works good ;-) + if (tx.count==0) + loop=1000000; + else + loop=tx.count; + + // this is for the statistics: + mz_start = clock(); + total_d = tx.count; + + + again: + + for (i=1; i<=loop; i++) + { + if (!simulate) libnet_write(l); + + if (verbose) + { + bs2str(root_id,dum1,8); + bs2str(bridge_id,dum2,8); + fprintf(stderr," sent BPDU: "); + fprintf(stderr,"%s ", (bpdumode==1) ? "conf" : "tcn "); + fprintf(stderr," id=%u ver=%u flags=%x rid=%s bid=%s\n" + " rpc=%u pid=%u age=%u maxage=%u hello=%u fwd_delay=%u\n", + id, + version, + flags, + dum1, + dum2, + root_pc, + port_id, + message_age, + max_age, + hello_time, + f_delay); + + fprintf(stderr,"\n"); + } + + + if (tx.delay) SLEEP (tx.delay); + } + + if (tx.count==0) + { + goto again; + } + + + libnet_destroy(l); + + return 0; +} + diff --git a/staging/layer3.c b/staging/layer3.c new file mode 100644 index 0000000..d05aa3c --- /dev/null +++ b/staging/layer3.c @@ -0,0 +1,734 @@ +/* + * 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 + * +*/ + + + +// *************************************************************************** +// This sections contains functions to send various L3-based PDUs such as +// +// * IP +// +// (ahem, yes this is currently all here...) +// +// *************************************************************************** + +#include "mz.h" +#include "cli.h" + +#define MZ_IP_HELP \ + "| IP type: Send raw IP packets.\n" \ + "|\n" \ + "| Supports L3 mode (automatic L2 creation) or 'L2-L3' mode (MAC addresses must be provided).\n" \ + "| In L3 mode the IP checksum and length cannot be manipulated to wrong values (currently).\n" \ + "| The L2-L3 mode is activated when specifying any MAC addresses on the command line\n" \ + "| (options -a, -b). \n" \ + "|\n" \ + "| The IP addresses can be specified via the -A and -B options, which identify the source\n" \ + "| and destination addresses, respectively. A dotted decimal notation, an IP range, or a\n" \ + "| FQDN can be used. The source address can also be random (-A rand).\n" \ + "|\n" \ + "| ARGUMENT SYNTAX: [<comma separated parameter list>]\n" \ + "|\n" \ + "| Parameters:\n" \ + "|\n" \ + "| len 0-65535 Only accessible in L2 mode\n" \ + "| sum 0-65535 Only accessible in L2 mode (0 means auto-calculation)\n" \ + "| tos 00-ff Full 8-bit control via hex input (use this also for ECN bits).\n" \ + "| dscp 0-63 Allows easier specification of DSCP (PHB and Drop Propability)\n" \ + "| ttl 0-255\n" \ + "| proto 0-255\n" \ + "| frag 0-65535 Includes flags (MSB) and offset (LSB)\n" \ + "| df Sets the \"Don't Fragment\" flag\n" \ + "| mf Sets the \"More Fragments\" flag\n" \ + "| rf Sets the reserved flag.\n" \ + "| id 0-65535\n" \ + "| loose <addresses> Loose Source Route (LSR) option; specify a sequence of hops\n" \ + "| using the notation: 1.1.1.1+2.2.2.2+3.3.3.3+...\n" \ + "| strict <addresses> Strict Source Route (SSR) option; same address notation as above\n" \ + "| option <hex_string> Specify any IP option using a hexadecimal string (aa:bb:cc:...)\n" \ + "|\n" \ + "| Additionally the Ethertype can be specified:\n" \ + "|\n" \ + "| ether_type 00:00-ff:ff Only accessible in L2 mode (default = 08:00 = IPv4)\n" \ + "| \n" + + +#define MZ_IP6_HELP \ + "| IP type: Send raw IPv6 packets.\n" \ + "|\n" \ + "| Supports L3 mode (automatic L2 creation) or 'L2-L3' mode (MAC addresses must be provided).\n" \ + "| In L3 mode the IP checksum and length cannot be manipulated to wrong values (currently).\n" \ + "| The L2-L3 mode is activated when specifying any MAC addresses on the command line\n" \ + "| (options -a, -b). \n" \ + "|\n" \ + "| ARGUMENT SYNTAX: [<comma separated parameter list>]\n" \ + "|\n" \ + "| Parameters:\n" \ + "|\n" \ + "| len 0-65535 Only accessible in L2 mode\n" \ + "| sum 0-65535 Only accessible in L2 mode (0 means auto-calculation)\n" \ + "| tos 00-ff Full 8-bit control via hex input (use this also for ECN bits).\n" \ + "| dscp 0-63 Allows easier specification of DSCP (PHB and Drop Propability)\n" \ + "| flow 0-1048575 Flow label\n" \ + "| hop 0-255 Hop limit\n" \ + "| next 0-255 Next protocol or header type\n" \ + "| frag 0-65535 Includes flags (MSB) and offset (LSB)\n" \ + "| mf Sets the \"More Fragments\" flag\n" \ + "| frag_res1 Sets the reserved flag 1.\n" \ + "| frag_res2 Sets the reserved flag 2.\n" \ + "| id 0-65535 Fragment ID\n" \ + "| loose <addresses> Source Routing Header\n" \ + "| rtype 0,2 Source Routing Type: 0 (Deprecated in RFC 5095) or 2 for Mobile IP\n" \ + "| segments 0-255 Number of route segments left, used by RH0\n" \ + "|\n" \ + "| Additionally the Ethertype can be specified:\n" \ + "|\n" \ + "| ether_type 00:00-ff:ff Only accessible in L2 mode (default = 86:dd = IPv6)\n" \ + "| \n" + + +// Only used to simplify initialization of libnet +// Return pointer to context +libnet_t* get_link_context() +{ + libnet_t * l; + char errbuf[LIBNET_ERRBUF_SIZE]; + + // Don't open context if only a help text is requested + if (getarg(tx.arg_string,"help", NULL)==1) + { + return NULL; + } + + + if (tx.packet_mode) + { // Let libnet create an appropriate Ethernet frame + if (ipv6_mode) + l = libnet_init (LIBNET_RAW6_ADV, tx.device, errbuf); + else + l = libnet_init (LIBNET_RAW4_ADV, tx.device, errbuf); + } + else // User specified Ethernet header details (src or dst) + { + l = libnet_init (LIBNET_LINK_ADV, tx.device, errbuf); + } + + if (l == NULL) + { + fprintf(stderr, "%s", errbuf); + exit(EXIT_FAILURE); + } + return l; +} + + +////////////////////////////////////////////////////////////////////////////// +// Prepare IP packet +libnet_ptag_t create_ip_packet (libnet_t *l) +{ + libnet_ptag_t t; + char argval[MAX_PAYLOAD_SIZE]; + int i, T; // only an abbreviation for tx.packet_mode + + if (ipv6_mode) + return create_ip6_packet(l); + + // Default IP header fields + tx.ip_len = LIBNET_IPV4_H; // Don't forget to add payload length + tx.ip_id = 0; + tx.ip_frag = 0; // Flags and Offset !!! + tx.ip_sum = 0; // default: automatically calculate checksum + tx.ip_tos = 0; + tx.ip_ttl = 255; + + + // temporary variables + unsigned int dummy; + size_t len; + char *s; + + + T = tx.packet_mode; // >0 means automatic L2 creation + + if ( (getarg(tx.arg_string,"help", NULL)==1) && (mode==IP) ) + { + if (mz_port) + { + cli_print(gcli, "%s", MZ_IP_HELP); + return -1; + } + else + { + + fprintf(stderr,"\n" + MAUSEZAHN_VERSION + "\n%s", MZ_IP_HELP); + + exit(0); + } + } + + // Check if hex_payload already specified (externally) + if (tx.hex_payload_s) + { + memcpy( (void*) tx.ip_payload, (void*) tx.hex_payload, tx.hex_payload_s); + tx.ip_payload_s = tx.hex_payload_s; + } + + + // Evaluate CLI parameters: + + if ( (getarg(tx.arg_string,"payload", argval)==1) || (getarg(tx.arg_string,"p", argval)==1)) + { + if (mode==IP) + tx.ip_payload_s = str2hex (argval, tx.ip_payload, MAX_PAYLOAD_SIZE); + } + // else payload has been specified as ASCII text via -P option + + + // NOTE: If 'mode' is NOT IP (e. g. UDP or TCP or something else) + // then the argument 'len' and 'sum' is NOT meant for the IP header! + // Instead the user can use 'iplen' and 'ipsum'. + if (mode==IP) + { + if (getarg(tx.arg_string,"len", argval)==1) + { + if (T) fprintf(stderr, " IP_Warning: 'len' cannot be set in this mode.\n"); + tx.ip_len = (u_int16_t) str2int(argval); + } + else + { + tx.ip_len = LIBNET_IPV4_H + tx.ip_payload_s; + } + + if (getarg(tx.arg_string,"sum", argval)==1) + { + if (T) fprintf(stderr, " IP_Warning: 'sum' cannot be set in this mode.\n"); + tx.ip_sum = (u_int16_t) str2int(argval); + } + } + else // mode is NOT IP + { + if (getarg(tx.arg_string,"iplen", argval)==1) + { + if (T) fprintf(stderr, " IP_Warning: 'len' cannot be set in this mode.\n"); + tx.ip_len = (u_int16_t) str2int(argval); + } + else + { + tx.ip_len = LIBNET_IPV4_H + tx.ip_payload_s; + } + + if (getarg(tx.arg_string,"ipsum", argval)==1) + { + if (T) fprintf(stderr, " IP_Warning: 'sum' cannot be set in this mode.\n"); + tx.ip_sum = (u_int16_t) str2int(argval); + } + } + + + if (getarg(tx.arg_string,"tos", argval)==1) + { + tx.ip_tos = (u_int8_t) strtol(argval,NULL,16); + dummy = (unsigned int) strtol(argval,NULL,16); + if (dummy > 255) fprintf(stderr, " IP_Warning: 'tos' too big, adjusted to LSBs\n"); + } + + if (getarg(tx.arg_string,"dscp", argval)==1) + { + dummy = (unsigned int) str2int(argval); + if (dummy > 63) + { + fprintf(stderr, " IP_Warning: 'dscp' too big, adjusted to 63\n"); + dummy = 63; + } + tx.ip_tos = (u_int8_t) dummy*4; + } + + if (getarg(tx.arg_string,"id", argval)==1) + { + tx.ip_id = (u_int16_t) str2int(argval); + } + + if (getarg(tx.arg_string,"frag", argval)==1) + { + tx.ip_frag = (u_int16_t) str2int(argval); + } + + if (getarg(tx.arg_string,"df", NULL)==1) + { + tx.ip_frag |= 0x4000; + } + + if (getarg(tx.arg_string,"mf", NULL)==1) + { + tx.ip_frag |= 0x2000; + } + + if (getarg(tx.arg_string,"rf", NULL)==1) + { + tx.ip_frag |= 0x8000; + } + + + if (getarg(tx.arg_string,"ttl", argval)==1) + { + tx.ip_ttl = (u_int8_t) str2int(argval); + } + + if (getarg(tx.arg_string,"proto", argval)==1) + { + tx.ip_proto = (u_int8_t) str2int(argval); + } + + + if ((tx.ascii)&&(mode==IP)) // ASCII PAYLOAD overrides hex payload + { + strncpy((char *)tx.ip_payload, (char *)tx.ascii_payload, MAX_PAYLOAD_SIZE); + tx.ip_payload_s = strlen((char *)tx.ascii_payload); + tx.ip_len += tx.ip_payload_s; + } + + + ///////// + // Want some padding? The specified number of padding bytes are ADDED to the + // payload. Note that this is only evaluated if we are in IP mode because + // UDP and TCP already might have been padded and set the ip_payload_s. + // (Note the difference in send_eth() where you specified the total number + // of bytes in the frame) + // + if ((tx.padding)&&(mode==IP)) + { + for (i=0; i<tx.padding; i++) + { + tx.ip_payload[tx.ip_payload_s+i] = 0x42; // pad with THE ANSWER (why random?) + } + tx.ip_payload_s += tx.padding; + tx.ip_len += tx.padding; + } + + + + + + // Loose and Strict Source Route + // See RFC 791 for most the detailed description + // + if ( (getarg(tx.arg_string,"loose", argval)==1) || + (getarg(tx.arg_string,"strict", argval)==1) ) + { + len = strlen(argval); + + if (len<7) // not even a single dotted decimal IP address given! + { + fprintf(stderr, " IP_Warning: Source route option requires at least one IP address!\n"); + // But we allow this :-) + } + + + // determine how many IP addresses have been specified + dummy=0; + for (i=0; i<len; i++) + { + if (ispunct(*(argval+i))) dummy++ ; + } + dummy = (dummy+1) / 4; // the number of IP addresses + + // Specify: type code, length, pointer + if (getarg(tx.arg_string,"loose", argval)==1) + { + tx.ip_option[0] = 131; // loose source route + } + else + { + tx.ip_option[0] = 137; // strict source route + } + tx.ip_option[1] = 3+(dummy*4); // length + tx.ip_option[2] = 4; // Use first IP address as next hop + //tx.ip_option[2] = 4+4*dummy; // smallest pointer, points to first address, which is + // the 4th byte within this option + + tx.ip_option_s = 3; + s = strtok(argval, ".+-:;/>"); + do + { + len--; + tx.ip_option[tx.ip_option_s] = (u_int8_t) str2int(s); + tx.ip_option_s++; + } while ( (s=strtok(NULL, ".+-:;/>")) != NULL ); + + tx.ip_option_s++; // EOL + + // add empty space for record route: //// NONSENSE? ///// + /* + for (i=0; i<(4*dummy); i++) + { + tx.ip_option[tx.ip_option_s] = 0x00; + tx.ip_option_s++; + } + */ + } + + + + // Allow any IP option specified as hex string + // An option can be a single byte or consist of multiple bytes in which case + // a length field is needed, see RFC 791. + if (getarg(tx.arg_string,"option", argval)==1) + { + // check if conflicting with argument "loose" or "strict" + if (tx.ip_option_s) + { + fprintf(stderr, " IP_Error: Another IP option already specified. Please check your arguments.\n"); + exit(1); + } + + tx.ip_option_s = str2hex (argval, tx.ip_option, 1023); + } + + + + if (tx.ip_option_s) + { + t = libnet_build_ipv4_options (tx.ip_option, + tx.ip_option_s, + l, + 0); + tx.ip_len += tx.ip_option_s; + } + + + /////// + // Did the user specify ANY payload? We require at least one byte! + /* + if (!tx.ip_payload_s) + { + tx.ip_payload[0] = 0x42; + tx.ip_payload_s = 1; + } + */ + + t = libnet_build_ipv4 (tx.ip_len, + tx.ip_tos, + tx.ip_id, + tx.ip_frag, + tx.ip_ttl, + tx.ip_proto, + tx.ip_sum, + tx.ip_src, // init.c defaults this to own SA + tx.ip_dst, // init.c defaults this to 255.255.255.255 + (mode==IP) ? (tx.ip_payload_s) ? tx.ip_payload : NULL : NULL, // if e.g. mode=UDP ignore payload argument + (mode==IP) ? tx.ip_payload_s : 0, + + /* + (mode==IP) ? tx.ip_payload : NULL, // if e.g. mode=UDP ignore payload argument + (mode==IP) ? tx.ip_payload_s : 0, + */ + l, + 0); + + + if (t == -1) + { + fprintf(stderr, " mz/create_ip_packet: Can't build IP header: %s\n", libnet_geterror(l)); + exit (0); + } + + + return t; + +} + +////////////////////////////////////////////////////////////////////////////// +// Prepare IPv6 packet +libnet_ptag_t create_ip6_packet (libnet_t *l) +{ + libnet_ptag_t t; + char argval[MAX_PAYLOAD_SIZE]; + int i, T; // only an abbreviation for tx.packet_mode + + // Default IP header fields + tx.ip_len = 0; + tx.ip_id = 0; + tx.ip6_segs = 0; + tx.ip6_rtype = 0; + tx.ip6_id = 0; + tx.ip_frag = 0; // Flags and Offset !!! + tx.ip_tos = 0; + tx.ip_ttl = 255; + + // temporary variables + unsigned int dummy; + size_t len; + char *s; + + T = tx.packet_mode; // >0 means automatic L2 creation + + if ( (getarg(tx.arg_string,"help", NULL)==1) && (mode==IP) ) + { + if (mz_port) + { + cli_print(gcli, "%s", MZ_IP6_HELP); + return -1; + } + else + { + fprintf(stderr,"\n" + MAUSEZAHN_VERSION + "\n%s", MZ_IP6_HELP); + + exit(0); + } + } + + // Check if hex_payload already specified (externally) + if (tx.hex_payload_s) + { + memcpy( (void*) tx.ip_payload, (void*) tx.hex_payload, tx.hex_payload_s); + tx.ip_payload_s = tx.hex_payload_s; + } + + // Evaluate CLI parameters: + if ( (getarg(tx.arg_string,"payload", argval)==1) || (getarg(tx.arg_string,"p", argval)==1)) + { + if (mode==IP) + tx.ip_payload_s = str2hex (argval, tx.ip_payload, MAX_PAYLOAD_SIZE); + } + // else payload has been specified as ASCII text via -P option + + // NOTE: If 'mode' is NOT IP (e. g. UDP or TCP or something else) + // then the argument 'len' and 'sum' is NOT meant for the IP header! + // Instead the user can use 'iplen' and 'ipsum'. + if (mode==IP) + { + if (getarg(tx.arg_string,"len", argval)==1) + { + if (T) fprintf(stderr, " IP_Warning: 'len' cannot be set in this mode.\n"); + tx.ip_len = (u_int16_t) str2int(argval); + } + else + { + tx.ip_len += tx.ip_payload_s; + } + } + else // mode is NOT IP + { + if (getarg(tx.arg_string,"iplen", argval)==1) + { + if (T) fprintf(stderr, " IP_Warning: 'len' cannot be set in this mode.\n"); + tx.ip_len = (u_int16_t) str2int(argval); + } + else + { + tx.ip_len += tx.ip_payload_s; + } + } + + + if (getarg(tx.arg_string,"tos", argval)==1) + { + tx.ip_tos = (u_int8_t) strtol(argval,NULL,16); + dummy = (unsigned int) strtol(argval,NULL,16); + if (dummy > 255) fprintf(stderr, " IP_Warning: 'tos' too big, adjusted to LSBs\n"); + } + + if (getarg(tx.arg_string,"flow", argval)==1) + { + dummy = (unsigned int) strtol(argval,NULL,16); + if (dummy > 1048575) + { + fprintf(stderr, " IP_Warning: 'flow label' too big, adjusted to 0xfffff\n"); + dummy = 0xfffff; + } + tx.ip_flow = dummy; + } + + if (getarg(tx.arg_string,"dscp", argval)==1) + { + dummy = (unsigned int) str2int(argval); + if (dummy > 63) + { + fprintf(stderr, " IP_Warning: 'dscp' too big, adjusted to 63\n"); + dummy = 63; + } + tx.ip_tos = (u_int8_t) dummy*4; + } + + if (getarg(tx.arg_string,"id", argval)==1) + { + tx.ip6_id = str2int(argval); + } + + if (getarg(tx.arg_string,"frag", argval)==1) + { + tx.ip_frag = ((u_int16_t) str2int(argval)) << 3; + } + + if (getarg(tx.arg_string,"mf", NULL)==1) + { + tx.ip_frag |= 0x0001; + } + + if (getarg(tx.arg_string,"frag_res1", NULL)==1) + { + tx.ip_frag |= 0x0002; + } + + if (getarg(tx.arg_string,"frag_res2", NULL)==1) + { + tx.ip_frag |= 0x0004; + } + + if (getarg(tx.arg_string,"hop", argval)==1) + { + tx.ip_ttl = (u_int8_t) str2int(argval); + } + + if (getarg(tx.arg_string,"next", argval)==1) + { + tx.ip_proto = (u_int8_t) str2int(argval); + } + else if (mode==IP) + { + tx.ip_proto = 59; // No Next Header for IPv6 + } + + + if ((tx.ascii)&&(mode==IP)) // ASCII PAYLOAD overrides hex payload + { + strncpy((char *)tx.ip_payload, (char *)tx.ascii_payload, MAX_PAYLOAD_SIZE); + tx.ip_payload_s = strlen((char *)tx.ascii_payload); + tx.ip_len += tx.ip_payload_s; + } + + + ///////// + // Want some padding? The specified number of padding bytes are ADDED to the + // payload. Note that this is only evaluated if we are in IP mode because + // UDP and TCP already might have been padded and set the ip_payload_s. + // (Note the difference in send_eth() where you specified the total number + // of bytes in the frame) + // + if ((tx.padding)&&(mode==IP)) + { + for (i=0; i<tx.padding; i++) + { + tx.ip_payload[tx.ip_payload_s+i] = 0x42; // pad with THE ANSWER (why random?) + } + tx.ip_payload_s += tx.padding; + tx.ip_len += tx.padding; + } + + if (tx.ip6_id) { + t = libnet_build_ipv6_frag (tx.ip_proto, + 0, + htons(tx.ip_frag), + htonl(tx.ip6_id), + (mode==IP) ? (tx.ip_payload_s) ? tx.ip_payload : NULL : NULL, + (mode==IP) ? tx.ip_payload_s : 0, + l, + 0); + tx.ip_len += LIBNET_IPV6_FRAG_H; + tx.ip_payload_s = 0; + tx.ip_proto = LIBNET_IPV6_NH_FRAGMENT; + } + + // See RFC 2460 Routing Header + // + if ( (getarg(tx.arg_string,"segments", argval)==1) ) + { + dummy = (unsigned int) str2int(argval); + if (dummy > 255) { + fprintf(stderr, " IP_Error: Maximal Routing Segments are 255!\n"); + exit(1); + } + tx.ip6_segs = dummy; + } + + if ( (getarg(tx.arg_string,"rtype", argval)==1) ) + { + dummy = (unsigned int) str2int(argval); + if (dummy > 255) { + fprintf(stderr, " IP_Error: Maximum Routing Type is 255!\n"); + exit(1); + } + tx.ip6_segs = dummy; + } + + if ( (getarg(tx.arg_string,"loose", argval)==1) ) + { + // Fill reserved + memset(tx.ip_option, 0, 4); + tx.ip_option_s=4; + + len = strlen(argval); + s = strtok(argval, ".+-;/>"); + do + { + len--; + *((struct libnet_in6_addr *) &tx.ip_option[tx.ip_option_s]) = libnet_name2addr6 (l, s, LIBNET_DONT_RESOLVE); + tx.ip_option_s += 16; + } while ( (s=strtok(NULL, ".+-;/>")) != NULL ); + + if (!tx.ip_option_s) { + fprintf(stderr, " IP_Error: No Routing Hops found!\n"); + exit(1); + } + + if (mode==IP && tx.ip_payload_s) + memmove(tx.ip_payload+tx.ip_option_s, tx.ip_payload, tx.ip_payload_s); + else + tx.ip_payload_s = 0; + + memcpy(tx.ip_payload, tx.ip_option, tx.ip_option_s); + tx.ip_payload_s += tx.ip_option_s; + + t = libnet_build_ipv6_routing(tx.ip_proto, + (tx.ip_option_s -4) / 8, + tx.ip6_rtype, + tx.ip6_segs, + tx.ip_payload, + tx.ip_payload_s, + l, + 0); + tx.ip_len += LIBNET_IPV6_ROUTING_H + tx.ip_option_s; + tx.ip_payload_s = 0; + tx.ip_proto = LIBNET_IPV6_NH_ROUTING; + } + + t = libnet_build_ipv6 (tx.ip_tos, + tx.ip_flow, + tx.ip_len, + tx.ip_proto, + tx.ip_ttl, + tx.ip6_src, + tx.ip6_dst, + (mode==IP) ? (tx.ip_payload_s) ? tx.ip_payload : NULL : NULL, + (mode==IP) ? tx.ip_payload_s : 0, + l, + 0); + + if (t == -1) + { + fprintf(stderr, " mz/create_ip_packet: Can't build IPv6 header: %s\n", libnet_geterror(l)); + exit (0); + } + + return t; +} + diff --git a/staging/layer4.c b/staging/layer4.c new file mode 100644 index 0000000..ca4d229 --- /dev/null +++ b/staging/layer4.c @@ -0,0 +1,884 @@ +/* + * 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 + * +*/ + + + +//////////////////////////////////////////////////////////////////// +// +// Layer 4 packet types +// +// 1. UDP +// 2. ICMP +// 3. TCP +// +//////////////////////////////////////////////////////////////////// + +#include "mz.h" +#include "cli.h" + + +#define MZ_UDP_HELP \ + "| UDP type: Send raw UDP packets.\n" \ + "|\n" \ + "| Parameters: \n" \ + "|\n" \ + "| sp 0-65535\n" \ + "| dp 0-65535\n" \ + "| len 0-65535\n" \ + "| udp_sum 0-65535\n" \ + "| payload|p <hex payload>\n" \ + "|\n" \ + "| Optionally the port numbers can be specified as ranges, e. g. \"dp=1023-33700\",\n" \ + "| in which case one packet per port number is sent.\n" \ + "|\n" \ + "| Note that the UDP length must include the header length. If you do NOT specify the len\n" \ + "| parameter (or specify len=0) then Mausezahn will compute the correct length.\n" \ + "|\n" \ + "| Note that all IP parameters can be modified (see IP help, i. e. '-t ip \"help\")\n" \ + "| except that (to avoid confusion) the IP length is 'iplen' and the IP checksum is 'ipsum'.\n" \ + "| Of course all Ethernet fields can also be accessed.\n" \ + "|\n" \ + "\n" + + +#define MZ_ICMP_HELP \ + "| ICMP type: Send raw ICMP packets.\n" \ + "|\n" \ + "| ARGUMENT SYNTAX: [type] <optional parameters> \n" \ + "| \n" \ + "| Per default an echo reply is sent (type=0, code=0)\n" \ + "|\n" \ + "| TYPE OPTIONAL PARAMETERS\n" \ + "| =========== ====================================================================\n" \ + "| Ping: \"ping\" or \"echoreq\" \n" \ + "| 'id' (0-65535) is the optional identification number\n" \ + "| 'seq' (0-65535) is the optional packet sequence number\n" \ + "|\n" \ + "| Redirect: \"redir, code=0, gw=192.168.1.10, p=aa:bb:cc\"\n" \ + "| 'gw' (or 'gateway') is the announced gateway, by default your own\n" \ + "| IP address.\n" \ + "| 'code' can be:\n" \ + "| 0 ... redirect datagram for the network\n" \ + "| 1 ... redirect datagram for the host\n" \ + "| 2 ... redirect datagram for ToS and network\n" \ + "| 3 ... redirect datagram for ToS and host\n" \ + "| 'p' (or 'payload') is the payload of the ICMP packet, tpyically an IP\n" \ + "| header. Note that - at the moment - you must prepare this payload by\n" \ + "| yourself.\n" \ + "|\n" \ + "| Unreachable \"unreach, code=2\"\n" \ + "| 'code' can be:\n" \ + "| 0 ... network unreachable\n" \ + "| 1 ... host unreachable\n" \ + "| 2 ... protocol unreachable\n" \ + "| 3 ... port unreachable\n" \ + "| 4 ... fragmentation needed but DF-bit is set\n" \ + "| 5 ... source route failed\n" \ + "|\n" \ + "|\n" \ + "| (other ICMP types will follow)\n" \ + "|\n" \ + "\n" + +#define MZ_ICMP6_HELP \ + "| ICMPv6 type: Send raw ICMPv6 packets.\n" \ + "|\n" \ + "| Parameters Values Explanation \n" \ + "| ---------- ------------------------------------ -------------------\n" \ + "| type 0-255 ICMPv6 Type\n" \ + "| code 0-255 ICMPv6 Code\n" \ + "| id 0-65535 optional identification number\n" \ + "| seq 0-65535 optional packet sequence number\n" \ + "| icmpv6_sum 0-65535 optional checksum\n" \ + "\n" + +#define MZ_TCP_HELP \ + "| TCP type: Send raw TCP packets.\n" \ + "|\n" \ + "| Parameters Values Explanation \n" \ + "| ---------- ------------------------------------ -------------------\n" \ + "| sp 0-65535 Source Port\n" \ + "| dp 0-65535 Destination Port\n" \ + "| flags fin|syn|rst|psh|ack|urg|ecn|cwr\n" \ + "| s 0-4294967295 Sequence Nr.\n" \ + "| a 0-4294967295 Acknowledgement Nr.\n" \ + "| win 0-65535 Window Size\n" \ + "| urg 0-65535 Urgent Pointer\n" \ + "| tcp_sum 0-65535 Checksum\n" \ + "|\n" \ + "| The port numbers can be specified as ranges, e. g. \"dp=1023-33700\".\n" \ + "| Multiple flags can be specified such as \"flags=syn|ack|urg\".\n" \ + "|\n" \ + "| Also the sequence number can be specified as a range, for example:\n" \ + "|\n" \ + "| s=10000-50000 ... send 40000 packets with SQNRs in that range. If the second\n" \ + "| value is lower than the first then it is assumed that the\n" \ + "| SQNRs should 'wrap around'.\n" \ + "| ds=30000 ........ use this increment within a SQNR-range.\n" \ + "|\n" \ + "| Note that all IP parameters can be modified (see IP help, i. e. '-t ip \"help\")\n" \ + "| except that (to avoid confusion) the IP length is 'iplen' and the IP checksum is 'ipsum'.\n" \ + "| Of course all Ethernet fields can also be accessed.\n"\ + "|\n" + + + +// Note: If another function specified tx.udp_payload then it must also +// set tx.udp_payload_s AND tx.udp_len = tx.udp_payload_s + 8 +libnet_ptag_t create_udp_packet (libnet_t *l) +{ + libnet_ptag_t t; + char argval[MAX_PAYLOAD_SIZE]; + int T; // only an abbreviation for tx.packet_mode + int i; + + ///////////////////////////// + // Default UDP header fields + // Already reset in init.c + ///////////////////////////// + + T = tx.packet_mode; // >0 means automatic L2 creation + + if ( (getarg(tx.arg_string,"help", NULL)==1) && (mode==UDP) ) + { + if (mz_port) + { + cli_print(gcli, "%s", MZ_UDP_HELP); + return -1; + } + else + { + + fprintf(stderr,"\n" + MAUSEZAHN_VERSION + "\n%s", MZ_UDP_HELP); + + exit(0); + } + + } + + + // Evaluate CLI parameters: + + if (getarg(tx.arg_string,"dp", argval)==1) + { + if (get_port_range (DST_PORT, argval)) // problem + { + tx.dp = 0; + } + } + + if (getarg(tx.arg_string,"sp", argval)==1) + { + if (get_port_range (SRC_PORT, argval)) // problem + { + tx.sp = 0; + } + } + + + // Check if hex_payload already specified (externally) + if (tx.hex_payload_s) + { + memcpy( (void*) tx.udp_payload, (void*) tx.hex_payload, tx.hex_payload_s); + tx.udp_payload_s = tx.hex_payload_s; + } + + if ( (getarg(tx.arg_string,"payload", argval)==1) || (getarg(tx.arg_string,"p", argval)==1)) + { + tx.udp_payload_s = str2hex (argval, tx.udp_payload, MAX_PAYLOAD_SIZE); + } + + + + if (getarg(tx.arg_string,"sum", argval)==1) + { + if (T) fprintf(stderr, " IP_Warning: 'sum' cannot be set in this mode.\n"); + tx.ip_sum = (u_int16_t) str2int(argval); + } + + if (getarg(tx.arg_string,"udp_sum", argval)==1) + { + tx.udp_sum = (u_int16_t) str2int(argval); + } + + + if (tx.ascii) // ASCII PAYLOAD overrides hex payload + { + strncpy((char *)tx.udp_payload, (char *)tx.ascii_payload, MAX_PAYLOAD_SIZE); + tx.udp_payload_s = strlen((char *)tx.ascii_payload); + printf("[%s]\n", tx.ascii_payload); + } + + + ///////// + // Want some padding? The specified number of padding bytes are ADDED to the + // payload. + // (Note the difference in send_eth() where you specified the total number + // of bytes in the frame) + // + if (tx.padding) + { + for (i=0; i<tx.padding; i++) + { + tx.udp_payload[tx.udp_payload_s+i] = 0x42; // pad with THE ANSWER (why random?) + } + tx.udp_payload_s += tx.padding; + } + + + + //////// + // The following is VERY IMPORTANT because the ip_payload_s is also set! + if (getarg(tx.arg_string,"len", argval)==1) + { + tx.udp_len = (u_int16_t) str2int(argval); + tx.ip_payload_s = tx.udp_len; + } + else // len NOT specified by user + { + if (tx.udp_len == 0) // len also not specified by another function (e. g. create_dns_packet...) + { + tx.udp_len = 8 + tx.udp_payload_s; + tx.ip_payload_s = tx.udp_len; + } + else // len (and payload and payload_s) has been specified by another function + { + tx.ip_payload_s = tx.udp_len; + } + + } + + + + t = libnet_build_udp(tx.sp, + tx.dp, + tx.udp_len, + tx.udp_sum, + (tx.udp_payload_s) ? tx.udp_payload : NULL, + tx.udp_payload_s, + l, + 0); + + // Checksum overwrite? Libnet IPv6 checksum calculation can't deal with extension headers, we have to do it ourself... + libnet_toggle_checksum(l, t, (tx.udp_sum || ipv6_mode) ? LIBNET_OFF : LIBNET_ON); + + if (t == -1) + { + fprintf(stderr, " mz/create_udp_packet: Can't build UDP header: %s\n", libnet_geterror(l)); + exit (0); + } + + + + return t; + +} + + + + + + + + +/////////////////////////////////////////////////// +/////////////////////////////////////////////////// +/////////////////////////////////////////////////// +/////////////////////////////////////////////////// +/////////////////////////////////////////////////// + + + +libnet_ptag_t create_icmp_packet (libnet_t *l) +{ + + libnet_ptag_t t; + char argval[MAX_PAYLOAD_SIZE]; + unsigned char *x; + + int i; + + enum + { + NONE, + ECHO_REQUEST, + REDIRECT, + UNREACHABLE + } + icmp; // which ICMP Type? + + + if ( (getarg(tx.arg_string,"help", NULL)==1) && (mode==ICMP) ) + { + if (mz_port) + { + cli_print(gcli, "%s", MZ_ICMP_HELP); + return -1; + } + else + { + + fprintf(stderr,"\n" + MAUSEZAHN_VERSION + "\n%s", MZ_ICMP_HELP); + exit(0); + } + } + + + ///////////////////////////////////////// + // + // Which ICMP Type has been specified? + // + // Note: to allow invalid type values we need the enum 'icmp' tp specify the sending function + // and the 'type' variable seperately. + + if ( (getarg(tx.arg_string,"redirect", NULL)==1) || (getarg(tx.arg_string,"redir", NULL)==1) ) + { + icmp = REDIRECT; + tx.icmp_type = ICMP_REDIRECT; + tx.icmp_code=ICMP_REDIRECT_HOST; + } + + + if ( (getarg(tx.arg_string,"ping", NULL)==1) || (getarg(tx.arg_string,"echoreq", NULL)==1) ) + { + icmp = ECHO_REQUEST; + tx.icmp_type = ICMP_ECHO; + tx.icmp_code = 0; + } + + + if (getarg(tx.arg_string,"unreach", NULL)==1) + { + icmp = UNREACHABLE; + tx.icmp_type = ICMP_UNREACH; + tx.icmp_code = 0; // network unreachable + } + + + ///////////////////////////////////////// + // + // Which parameters have been specified? + + + if (getarg(tx.arg_string,"type", argval)==1) + { + tx.icmp_type = (u_int8_t) str2int(argval); + } + + if (getarg(tx.arg_string,"code", argval)==1) + { + tx.icmp_code = (u_int8_t) str2int(argval); + } + else + { + // Use appropriate defaults depending on ICMP type + } + + + if (getarg(tx.arg_string,"icmp_sum", argval)==1) + { + tx.icmp_chksum = (u_int16_t) str2int(argval); + } + + if ( (getarg(tx.arg_string,"gateway", argval)==1) || (getarg(tx.arg_string,"gw", argval)==1) ) + { + tx.icmp_gateway = str2ip32 (argval); + } + else + { + tx.icmp_gateway = tx.ip_src; // prefer own address + } + + + if (getarg(tx.arg_string,"id", argval)==1) + { + tx.icmp_ident = (u_int16_t) str2int(argval); + } + + if (getarg(tx.arg_string,"seq", argval)==1) + { + tx.icmp_sqnr = (u_int16_t) str2int(argval); + } + + + // Check if hex_payload already specified (externally) + if (tx.hex_payload_s) + { + memcpy( (void*) tx.icmp_payload, (void*) tx.hex_payload, tx.hex_payload_s); + tx.icmp_payload_s = tx.hex_payload_s; + } + + + if ( (getarg(tx.arg_string,"payload", argval)==1) || (getarg(tx.arg_string,"p", argval)==1)) + { + tx.icmp_payload_s = str2hex (argval, tx.icmp_payload, MAX_PAYLOAD_SIZE); + } + else + { + tx.icmp_payload_s = 0; + } + + + if (tx.ascii) // ASCII PAYLOAD overrides hex payload + { + strncpy((char *)tx.icmp_payload, (char *)tx.ascii_payload, MAX_PAYLOAD_SIZE); + tx.icmp_payload_s = strlen((char *)tx.ascii_payload); + } + + + ///////// + // Want some padding? The specified number of padding bytes are ADDED to the + // payload. + // (Note the difference in send_eth() where you specified the total number + // of bytes in the frame) + // + if (tx.padding) + { + for (i=0; i<tx.padding; i++) + { + tx.icmp_payload[tx.icmp_payload_s+i] = 0x42; // pad with THE ANSWER (why random?) + } + tx.icmp_payload_s += tx.padding; + } + + + //////////////////////////////////////////////////////////////////////////////////////////// + // + // Now determine which type of ICMP packet to send. + // + // NOTE: Every section (icmp-type) must provide + // + // 1. a build function + // 2. tx.ip_payload_s which indicates the whole ICMP packet size + // 3. tx.icmp_verbose_string containing details about the ICMP packet (verbose mode) + // + //////////////////////////////////////////////////////////////////////////////////////////// + + switch (icmp) + { + case REDIRECT: // +++++++++++++++ + t = libnet_build_icmpv4_redirect (tx.icmp_type, + tx.icmp_code, + tx.icmp_chksum, + tx.icmp_gateway, + (tx.icmp_payload_s) ? tx.icmp_payload : NULL, + tx.icmp_payload_s, + l, + 0); + tx.ip_payload_s = LIBNET_ICMPV4_REDIRECT_H + tx.icmp_payload_s; // for send_ip + if (verbose) + { + x = (unsigned char*) &tx.icmp_gateway; + sprintf(tx.icmp_verbose_txt,"ICMP Redirect, GW=%u.%u.%u.%u", + *(x),*(x+1),*(x+2),*(x+3)); + } + break; // ++++++++++++++++++++++ + case NONE: + case ECHO_REQUEST: + t = libnet_build_icmpv4_echo(tx.icmp_type, + tx.icmp_code, + tx.icmp_chksum, + tx.icmp_ident, + tx.icmp_sqnr, + (tx.icmp_payload_s) ? tx.icmp_payload : NULL, + tx.icmp_payload_s, + l, + 0); + tx.ip_payload_s = LIBNET_ICMPV4_REDIRECT_H + tx.icmp_payload_s; // for send_ip + if (verbose) + { + if (icmp == NONE) + sprintf(tx.icmp_verbose_txt,"ICMP Type %u Code %u\n",tx.icmp_type,tx.icmp_code); + else + sprintf(tx.icmp_verbose_txt,"ICMP Echo Request (id=%u seq=%u)\n",tx.icmp_ident,tx.icmp_sqnr); + } + break; // ++++++++++++++++++++++ + case UNREACHABLE: + t = libnet_build_icmpv4_unreach(tx.icmp_type, + tx.icmp_code, + tx.icmp_chksum, + (tx.icmp_payload_s) ? tx.icmp_payload : NULL, + tx.icmp_payload_s, + l, + 0); + if (verbose) + { + sprintf(tx.icmp_verbose_txt,"ICMP unreachable (code=%u)\n",tx.icmp_code); + } + break; // ++++++++++++++++++++++ + default: + (void) fprintf(stderr," mz/icmp: unknown mode! Stop.\n"); + return (1); + } + + libnet_toggle_checksum(l, t, tx.icmp_chksum ? LIBNET_OFF : LIBNET_ON); + + if (t == -1) + { + fprintf(stderr, " mz/create_icmp_packet: Can't build ICMP header: %s\n", libnet_geterror(l)); + exit (0); + } + + + return t; +} + +libnet_ptag_t create_icmp6_packet (libnet_t *l) +{ + libnet_ptag_t t; + char argval[MAX_PAYLOAD_SIZE]; + + int i; + tx.icmp_ident = 0; + tx.icmp_sqnr = 0; + + if ( (getarg(tx.arg_string,"help", NULL)==1) && (mode==ICMP) ) + { + if (mz_port) + { + cli_print(gcli, "%s", MZ_ICMP6_HELP); + return -1; + } + else + { + fprintf(stderr,"\n" + MAUSEZAHN_VERSION + "\n%s", MZ_ICMP6_HELP); + exit(0); + } + } + + + ///////////////////////////////////////// + // + // Which parameters have been specified? + + + if (getarg(tx.arg_string,"type", argval)==1) + { + tx.icmp_type = (u_int8_t) str2int(argval); + } + + if (getarg(tx.arg_string,"code", argval)==1) + { + tx.icmp_code = (u_int8_t) str2int(argval); + } + + if (getarg(tx.arg_string,"id", argval)==1) + { + tx.icmp_ident = (u_int16_t) str2int(argval); + } + + if (getarg(tx.arg_string,"seq", argval)==1) + { + tx.icmp_sqnr = (u_int16_t) str2int(argval); + } + + if (getarg(tx.arg_string,"icmpv6_sum", argval)==1) + { + tx.icmp_chksum = (u_int16_t) str2int(argval); + } + + // Check if hex_payload already specified (externally) + if (tx.hex_payload_s) + { + memcpy( (void*) tx.icmp_payload, (void*) tx.hex_payload, tx.hex_payload_s); + tx.icmp_payload_s = tx.hex_payload_s; + } + + if ( (getarg(tx.arg_string,"payload", argval)==1) || (getarg(tx.arg_string,"p", argval)==1)) + { + tx.icmp_payload_s = str2hex (argval, tx.icmp_payload, MAX_PAYLOAD_SIZE); + } + else + { + tx.icmp_payload_s = 0; + } + + if (tx.ascii) // ASCII PAYLOAD overrides hex payload + { + strncpy((char *)tx.icmp_payload, (char *)tx.ascii_payload, MAX_PAYLOAD_SIZE); + tx.icmp_payload_s = strlen((char *)tx.ascii_payload); + } + + ///////// + // Want some padding? The specified number of padding bytes are ADDED to the + // payload. + // (Note the difference in send_eth() where you specified the total number + // of bytes in the frame) + // + if (tx.padding) + { + for (i=0; i<tx.padding; i++) + { + tx.icmp_payload[tx.icmp_payload_s+i] = 0x42; // pad with THE ANSWER (why random?) + } + tx.icmp_payload_s += tx.padding; + } + + sprintf(tx.icmp_verbose_txt,"ICMPv6 Type %u Code %u\n",tx.icmp_type,tx.icmp_code); + + t = libnet_build_icmpv4_echo (tx.icmp_type, + tx.icmp_code, + tx.icmp_chksum, + tx.icmp_ident, + tx.icmp_sqnr, + tx.icmp_payload_s ? tx.icmp_payload : NULL, + tx.icmp_payload_s, + l, + 0); + tx.ip_payload_s = LIBNET_ICMPV6_H + tx.icmp_payload_s; // for send_ip + + // Libnet IPv6 checksum calculation can't deal with extension headers, we have to do it ourself... + libnet_toggle_checksum(l, t, (tx.icmp_chksum || ipv6_mode) ? LIBNET_OFF : LIBNET_ON); + + if (t == -1) + { + fprintf(stderr, " mz/create_icmp_packet: Can't build ICMPv6 header: %s\n", libnet_geterror(l)); + exit (0); + } + + return t; +} + + + +/////////////////////////////////////////////////// +/////////////////////////////////////////////////// +/////////////////////////////////////////////////// +/////////////////////////////////////////////////// +/////////////////////////////////////////////////// + + +// Note: If another function specified tx.tcp_payload then it must also +// set tx.tcp_payload_s AND tx.tcp_len = tx.tcp_payload_s + 20 +libnet_ptag_t create_tcp_packet (libnet_t *l) +{ + libnet_ptag_t t, t2; + char argval[MAX_PAYLOAD_SIZE], *dummy1, *dummy2; + int T; // only an abbreviation for tx.packet_mode + int i; + + u_int8_t tcp_default_options[] = + { + 0x02, 0x04, 0x05, 0xac, // MSS + 0x04, 0x02, // SACK permitted + 0x08, 0x0a, 0x19, 0x35, 0x90, 0xc3, 0x00, 0x00, 0x00, 0x00, // Timestamps + 0x01, // NOP + 0x03, 0x03, 0x05 // Window Scale 5 + }; + + + + ///////////////////////////// + // Default TCP header fields + // Already reset in init.c + ///////////////////////////// + + T = tx.packet_mode; // >0 means automatic L2 creation + + if ( (getarg(tx.arg_string,"help", NULL)==1) && (mode==TCP) ) + { + if (mz_port) + { + cli_print(gcli, "%s", MZ_TCP_HELP); + return -1; + } + else + { + fprintf(stderr,"\n" + MAUSEZAHN_VERSION + "\n%s", MZ_TCP_HELP); + exit(0); + } + } + + + // Evaluate CLI parameters: + + if (getarg(tx.arg_string,"dp", argval)==1) + { + if (get_port_range (DST_PORT, argval)) // problem + { + tx.dp = 0; + } + } + + + if (getarg(tx.arg_string,"sp", argval)==1) + { + if (get_port_range (SRC_PORT, argval)) // problem + { + tx.sp = 0; + } + } + + + if (getarg(tx.arg_string,"s", argval)==1) + { + //check whether a range has been specified: + dummy1 = strtok(argval, "-"); + tx.tcp_seq = (u_int32_t) str2int (dummy1); + if ( (dummy2 = strtok(NULL, "-")) == NULL ) // no additional value + { + tx.tcp_seq_stop = tx.tcp_seq; + } + else // range + { + tx.tcp_seq_stop = (u_int32_t) str2int (dummy2); + tx.tcp_seq_start = tx.tcp_seq; // initially tcp_seq = tcp_seq_start + tx.tcp_seq_delta = 1; // an initialization only in case 'ds' not specified + } + } + + if (getarg(tx.arg_string,"ds", argval)==1) + { + tx.tcp_seq_delta = (u_int32_t) str2int (argval); + } + + if (getarg(tx.arg_string,"a", argval)==1) + { + tx.tcp_ack = (u_int32_t) str2int (argval); + } + + if (getarg(tx.arg_string,"win", argval)==1) + { + tx.tcp_win = (u_int16_t) str2int (argval); + } + + if (getarg(tx.arg_string,"urg", argval)==1) + { + tx.tcp_urg = (u_int16_t) str2int (argval); + } + + + if ( (getarg(tx.arg_string,"flags", argval)==1) || + (getarg(tx.arg_string,"flag", argval)==1) ) // because everybody confuses this + { + if (get_tcp_flags(argval)) // problem + { + tx.tcp_control=2; // Assume SYN as default + } + } + + if (getarg(tx.arg_string,"tcp_sum", argval)==1) + { + tx.tcp_sum = (u_int16_t) str2int(argval); + } + + // Check if hex_payload already specified (externally) + if (tx.hex_payload_s) + { + memcpy( (void*) tx.tcp_payload, (void*) tx.hex_payload, tx.hex_payload_s); + tx.tcp_payload_s = tx.hex_payload_s; + } + + + if ( (getarg(tx.arg_string,"payload", argval)==1) || (getarg(tx.arg_string,"p", argval)==1)) + { + tx.tcp_payload_s = str2hex (argval, tx.tcp_payload, MAX_PAYLOAD_SIZE); + } + + + if (tx.ascii) // ASCII PAYLOAD overrides hex payload + { + strncpy((char *)tx.tcp_payload, (char *)tx.ascii_payload, MAX_PAYLOAD_SIZE); + tx.tcp_payload_s = strlen((char *)tx.ascii_payload); + tx.tcp_len = 20 + tx.tcp_payload_s; // only needed by libnet to calculate checksum + tx.ip_payload_s = tx.tcp_len; // for create_ip_packet + } + + + + ///////// + // Want some padding? The specified number of padding bytes are ADDED to the + // payload. + // (Note the difference in send_eth() where you specified the total number + // of bytes in the frame) + // + if (tx.padding) + { + for (i=0; i<tx.padding; i++) + { + tx.tcp_payload[tx.tcp_payload_s+i] = 0x42; // pad with THE ANSWER (why random?) + } + tx.tcp_payload_s += tx.padding; + + } + + + + tx.tcp_len = 20 + tx.tcp_payload_s; // only needed by libnet to calculate checksum + tx.ip_payload_s = tx.tcp_len; // for create_ip_packet + + if (tx.tcp_control & 0x02) // packets with syn require an MSS option + { + t2 = libnet_build_tcp_options(tcp_default_options, + 20, + l, + 0); + + if (t2 == -1) + { + fprintf(stderr, " mz/create_tcp_packet: Can't build TCP options: %s\n", libnet_geterror(l)); + exit (0); + } + + tx.tcp_len += 20; + tx.tcp_offset = 10; + tx.ip_payload_s = tx.tcp_len; // for create_ip_packet + tx.tcp_sum_part = libnet_in_cksum((u_int16_t *) tcp_default_options, 20); + } + else + { + tx.tcp_offset = 5; + tx.tcp_sum_part = 0; + } + + t = libnet_build_tcp (tx.sp, + tx.dp, + tx.tcp_seq, + tx.tcp_ack, + tx.tcp_control, + tx.tcp_win, + tx.tcp_sum, + tx.tcp_urg, + tx.tcp_len, + (tx.tcp_payload_s) ? tx.tcp_payload : NULL, + tx.tcp_payload_s, + l, + 0); + + + + // Libnet IPv6 checksum calculation can't deal with extension headers, we have to do it ourself... + libnet_toggle_checksum(l, t, (tx.tcp_sum || ipv6_mode) ? LIBNET_OFF : LIBNET_ON); + + if (t == -1) + { + fprintf(stderr, " mz/create_tcp_packet: Can't build TCP header: %s\n", libnet_geterror(l)); + exit (0); + } + + + return t; +} diff --git a/staging/llist.c b/staging/llist.c new file mode 100644 index 0000000..d729e46 --- /dev/null +++ b/staging/llist.c @@ -0,0 +1,176 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 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 + * +*/ + + +#include "mz.h" +#include "cli.h" +#include "mops.h" +#include "llist.h" + +/* PURPOSE: + * General doubly linked list with management functions. + * + * NOTE: + * There is no dummy head element. Every element may contain data! + * Therefore there is only one general "create_new_element" function. + * + * You cannot delete the head element except you want to delete the whole list. + * Usually you delete the head element at last. + * + * head->refcount always contains the number of elements. + * + * Each element has a unique index number. + * + * The user must assign her/his data to (void*) elem->data. + * + */ + + +// Create new list element - may be the first one (list==NULL) +// +struct mz_ll * mz_ll_create_new_element(struct mz_ll *list) +{ + struct mz_ll *new_element; + new_element = (struct mz_ll*) malloc (sizeof(struct mz_ll)); + if (new_element==NULL) return NULL; + _mz_ll_set_default(new_element); + if (list==NULL) { + new_element->next=new_element; + new_element->prev=new_element; + new_element->head=new_element; + new_element->refcount=1; + new_element->index=0; + new_element->index_last=0; + } else { + new_element->prev=list->prev; + new_element->next=list; + new_element->prev->next=new_element; + list->prev = new_element; + new_element->head=list; + list->refcount++; + list->index_last++; + new_element->index=list->index_last; + } + + return new_element; +} + +// Delete ONE list element. +int mz_ll_delete_element (struct mz_ll *cur) +{ + if ((cur==NULL)||(cur==cur->head)) return -1; // don't delete head! + if (cur->data!=NULL) { free(cur->data); cur->data=NULL; } + + if ((cur->next!=cur)&&(cur->prev!=cur)) { + cur->prev->next=cur->next; + cur->next->prev=cur->prev; + } + cur->head->refcount--; + if (cur!=NULL) { free(cur); cur=NULL; } + return 0; +} + + +int mz_ll_delete_list (struct mz_ll *list) +{ + struct mz_ll *cur=list, + *tmp; + + if (cur==NULL) return 1; + while (cur!=cur->next) { + tmp=cur->next; + mz_ll_delete_element(cur); + cur=tmp; + } + // Finally free list head: + if (list->data!=NULL) { free(list->data); list->data=NULL; } + free(list); + list=NULL; + return 0; +} + +struct mz_ll * mz_ll_search_name (struct mz_ll *list, char *str) +{ + struct mz_ll *cur=list; + do { + if (strncmp(cur->name, str, MZ_LL_NAME_LEN)==0) return cur; + cur=cur->next; + } + while (cur!=list); + return NULL; +} + +struct mz_ll * mz_ll_search_index (struct mz_ll *list, int i) +{ + struct mz_ll *cur=list; + do { + if (cur->index==i) return cur; + cur=cur->next; + } + while (cur!=list); + return NULL; +} + +int mz_ll_size(struct mz_ll *list) +{ + int i=0; + struct mz_ll *cur=list; + + if (list==NULL) return 0; + + do { + i++; + cur=cur->next; + } + while (cur!=list); + if (i!=list->refcount) fprintf(stderr, "MZ_LL_SIZE: Anomalous situation. Report this.\n"); + return i; +} + + +int mz_ll_dump_all(struct mz_ll *list) +{ + int i=0; + struct mz_ll *cur=list; + + if (list==NULL) return 0; + + do { + i++; + fprintf(stdout, "Element %i: '%s', index=%i\n",i,cur->name, cur->index); + cur=cur->next; + } + while (cur!=list); + return i; +} + + + +// ------ PRIVATE: initialize list-element +void _mz_ll_set_default (struct mz_ll *cur) +{ + cur->refcount = 0; + cur->data = NULL; + cur->name[0]='\0'; + cur->index=0; + cur->state=0; +} + + + + diff --git a/staging/llist.h b/staging/llist.h new file mode 100644 index 0000000..49a87c7 --- /dev/null +++ b/staging/llist.h @@ -0,0 +1,75 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 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 + * +*/ + + + +#ifndef MZ_LINKED_LIST +#define MZ_LINKED_LIST + +#define MAX_PACKET_SEQUENCE_LEN 20 // how many packets can be defined in a sequence at maximum + +// A packet sequence -- this is the list data (each list element corresponds to one sequence) +struct pseq { + struct mops *packet[MAX_PACKET_SEQUENCE_LEN]; // pointer to the packets + struct timespec gap[MAX_PACKET_SEQUENCE_LEN]; // optional delay between different packets + int count; // total number of current members (=packets) +}; + + +// --------------- Mausezahn Multipurpose Linked List: ------------------- + +#define MZ_LL_NAME_LEN 64 + +// one list element +struct mz_ll { + struct mz_ll *prev; + struct mz_ll *next; + struct mz_ll *head; // always points to head element + int refcount; // head element: total number of list items! (Otherwise can be used as refcount.) + char name[MZ_LL_NAME_LEN]; + pthread_t sequence_thread; + int state; // 0 = inactive, 1 = active + int index; // monotonically increasing; + int index_last; //head always stores the last value! + void *data; // points to your data +}; + +struct mz_ll *packet_sequences; +struct mz_ll *cli_seq; // currently edited packet sequence used by CLI + +// prototypes +struct mz_ll * mz_ll_create_new_element(struct mz_ll *list); +int mz_ll_delete_element (struct mz_ll *cur); +int mz_ll_delete_list(struct mz_ll *list); +struct mz_ll * mz_ll_search_name (struct mz_ll *list, char *str); +void _mz_ll_set_default (struct mz_ll *cur); +int mz_ll_dump_all(struct mz_ll *list); +int mops_tx_sequence (struct mz_ll *seq); + +// convenience functions using the above in a more intelligent way +int mops_delete_sequence(char *name); +struct mz_ll * mops_create_sequence (char *name); +int mops_dump_sequence (char* str); +int mops_add_packet_to_sequence (struct mz_ll *seq, struct mops *mp); +int mops_add_delay_to_sequence (struct mz_ll *seq, struct timespec *t); +int mops_delete_packet_from_pseq (struct mz_ll *seq, int index); +int mops_delete_all_packets_from_pseq (struct mz_ll *seq); +int stop_sequence (char *name); +int stop_all_sequences (); +#endif + diff --git a/staging/lookupdev.c b/staging/lookupdev.c new file mode 100644 index 0000000..dfca239 --- /dev/null +++ b/staging/lookupdev.c @@ -0,0 +1,357 @@ +/* + * 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 + * +*/ + + + + +#include "mz.h" +#include "mops.h" + +#include <netpacket/packet.h> +#include <netinet/ether.h> + + +// PURPOSE: Find usable network devices +// +// NOTE: +// +// 1. Ignores devices without IP address +// 2. Ignores loopback (etc) +// +// RETURN VALUES: +// +// 0 if usable device found (device_list[] and tx.device set) +// 1 if no usable device found +// +int lookupdev() +{ + // char *tx.device is global, see as.h + + char + ipaddress[IPADDRSIZE+1], + errbuf[PCAP_ERRBUF_SIZE]; + + pcap_if_t + *alldevs, + *index = NULL; + + pcap_addr_t *pcap_addr; + + int i=0; + + + // FIRST get a list of all available devices + // + if (pcap_findalldevs(&alldevs, errbuf) == -1) + { + fprintf(stderr," mz: %s\n",errbuf); + return 1; + } + + index = (pcap_if_t *) alldevs; + + while (index) + { + if (index->addresses) + { + pcap_addr = index->addresses; + while(pcap_addr) + { + if (pcap_addr->addr && (pcap_addr->addr->sa_family==AF_INET)) + { + if (inet_ntop(pcap_addr->addr->sa_family, + (void *)&pcap_addr->addr->sa_data[2], + ipaddress, + IPADDRSIZE)) + { + if (verbose) + { + fprintf(stderr," mz: device %s got assigned %s ", + index->name, ipaddress); + } + + if (strncmp(ipaddress, "127", 3)==0) + { + if (verbose) fprintf(stderr, "(loopback)\n"); + strncpy(device_list[i].dev, index->name, 9); + strncpy(device_list[i].ip_str, ipaddress, IPADDRSIZE); + device_list[i].phy=0; + get_if_addr(index->name, device_list[i].ip, device_list[i].mac); + get_if_addr(index->name, device_list[i].ip_mops, device_list[i].mac_mops); + i++; + } + else if (strncmp(ipaddress, "169.254", 7)==0) + { + if (verbose) fprintf(stderr, "but IGNORED (cause: host-scope address)\n"); + } + else // FOUND VALID INTERFACE + { + if (verbose) fprintf(stderr, "and is a possible candidate.\n"); + strncpy(device_list[i].dev, index->name, 9); + strncpy(device_list[i].ip_str, ipaddress, IPADDRSIZE); + device_list[i].phy=1; + get_if_addr(index->name, device_list[i].ip, device_list[i].mac); + get_if_addr(index->name, device_list[i].ip_mops, device_list[i].mac_mops); + i++; + } + + // Select only interfaces with IP addresses + // but avoid those that start with 127 or 169.254 + // Put the remaining on a list. If this list has more than one entry + // ask the user which interface to listen to. + } + else + { + return 1; + } + } + pcap_addr = pcap_addr->next; + } // closes while(pcap_addr) + } + index = index->next; + } // closes while (index) + + device_list_entries = i; + + /* + if (verbose) + { + for (i=0; i<device_list_entries; i++) + { + fprintf(stderr, " mz: Found device %s with IP %s\n", device_list[i].dev, device_list[i].ip_str); + } + } + */ + + // No device found: + if (device_list_entries==0) return 1; + + // Else device found: + // initialize tx.device with first entry of the device_list + strncpy (tx.device, device_list[0].dev, 16); + + return 0; +} + + + + + + + + +// Determines ip and mac address of specified interface 'ifname' +// Caller must provide an unsigned char ip[4], mac[6] +// +int get_if_addr (char *ifname, u_int8_t *ip, u_int8_t *mac) +{ + int fd, i; + struct ifreq ifr; + struct sockaddr_in saddr; + u_int8_t *x; + + ifr.ifr_addr.sa_family = AF_INET; + strncpy(ifr.ifr_name, ifname , IFNAMSIZ-1); + + // we must open a socket to get the addresses + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd == -1) return 1; + + // get mac + ioctl(fd, SIOCGIFHWADDR, &ifr); + for (i=0; i<6; i++) mac[i]= (u_int8_t) ifr.ifr_hwaddr.sa_data[i]; + + // get IP + ioctl(fd, SIOCGIFADDR, &ifr); + saddr=*((struct sockaddr_in *)(&(ifr.ifr_addr))); + x = (u_int8_t*)&saddr.sin_addr; + ip[0]=*x; ip[1]=*(x+1); ip[2]=*(x+2); ip[3]=*(x+3); + + close(fd); + + + return 0; +} + + + + +// For a given device name, find out the following parameters: +// +// - MTU +// - Network +// - Mask +// - Default GW (IP) +// - Default GW (MAC) +// - Open packet socket (if not already done) +// +int get_dev_params (char *name) +{ + FILE *fd; + + char f[10][16], line[256]; + int in=0, nw=1, gw=2, mk=7; // default columns in /proc/net/route for interface, network, gateway, and mask. + unsigned int tmp[4], net[4]={0,0,0,0}, dgw[4], mask[4]={0,0,0,0}; + int i=0, flag=0, nw_found=0, gw_found=0, devind=0, dev_found=0; + + struct ifreq si; + struct sockaddr_ll psock; + int ps, index, mtu; + struct arp_table_struct *cur; + // 1. Check if device is already present in our device_list + + for (i=0; i<device_list_entries; i++) { + if (strncmp(device_list[i].dev, name, 16)==0) { + devind=i; + dev_found=1; + break; + } + } + if (dev_found==0) return 1; // ERROR: device name not found !!!! + + + + // 2. find network, gateway, and mask + + fd = fopen("/proc/net/route", "r"); + while (fgets(line, 255, fd)!=NULL) { + sscanf(line, " %s %s %s %s %s %s %s %s %s %s", f[0], f[1], f[2], f[3], f[4], f[5], f[6], f[7], f[8], f[9]); + if (!flag) { // find columns (we do NOT assume that the order of columns is the same everywhere) + for (i=0; i<10; i++) { + if (strncasecmp(f[i],"iface", 16)==0) in=i; + if (strncasecmp(f[i],"destination", 16)==0) nw=i; + if (strncasecmp(f[i],"gateway", 16)==0) gw=i; + if (strncasecmp(f[i],"mask", 16)==0) mk=i; + } + flag=1; + } + + if (strncmp(f[in], name, 16)==0) { // interface found + // Determine network + if ((strncmp(f[nw],"00000000",8)!=0) && (strncmp(f[gw],"00000000",8)==0)) { + // ignore 169.254 and 127 networks + sscanf(f[nw],"%02x%02x%02x%02x",&tmp[3], &tmp[2], &tmp[1], &tmp[0]); + if ((tmp[0]!=127) && (tmp[0]!=169)) { + nw_found=1; + net[0]=tmp[0]; + net[1]=tmp[1]; + net[2]=tmp[2]; + net[3]=tmp[3]; + // also get mask for that network + sscanf(f[mk],"%02x%02x%02x%02x",&tmp[3], &tmp[2], &tmp[1], &tmp[0]); + mask[0]=tmp[0]; + mask[1]=tmp[1]; + mask[2]=tmp[2]; + mask[3]=tmp[3]; + } + } + // Determine gateway + if ((strncmp(f[nw],"00000000",8)==0) && (strncmp(f[gw],"00000000",8)!=0)) { + sscanf(f[gw],"%02x%02x%02x%02x",&dgw[3], &dgw[2], &dgw[1], &dgw[0]); + gw_found=1; + } + } + } + + fclose(fd); + + + // 3. Get device index, determine MTU, + // and bind socket to device for later TX and RX + + // if socket is already open, then close and re-open it! + if (device_list[devind].ps>=0) { + close(device_list[devind].ps); + device_list[devind].ps=-1; + } + + if (device_list[devind].ps<0) { + ps = socket (PF_PACKET, SOCK_RAW, htons(ETH_P_IP)); //ETH_P_ALL, ETH_P_802_3); + if (ps<0) { + fprintf(stderr, " Warning: [lookupdev.c get_dev_params()] Cannot open socket!\n"); + return 1; + } + + // Get device index + strncpy(si.ifr_name, name, IFNAMSIZ); + if (ioctl(ps, SIOCGIFINDEX, &si)==-1) { + perror("ioctl"); + close(ps); + return 1; + } + index=si.ifr_ifindex; + + // Get MTU + if (ioctl(ps, SIOCGIFMTU, &si)==-1) { + perror("ioctl"); + close(ps); + return 1; + } + mtu = si.ifr_mtu; + + // ***** bind socket for later TX and RX **** + psock.sll_family = AF_PACKET; // evident + // psock.sll_protocol = 0; // unsigned short - Physical layer protocol + psock.sll_ifindex = index; // int - Interface number + psock.sll_hatype = 0; // unsigned short - Header type //ARPHRD_ETHER + psock.sll_pkttype = 0; // unsigned char - Packet type + psock.sll_halen = 6; // unsigned char - Length of address + bind(ps, (const struct sockaddr *) &psock, sizeof(psock)); // <= !!! + device_list[devind].ps = ps; // Note that close(ps) must be done upon termination + } + + // Get MAC of default gateway + service_arp(name, device_list[devind].ip_gw, device_list[devind].mac_gw); + + usleep(200); // this is a VERY short delay but it usually works in today's LANs + cur=device_list[devind].arp_table; + while(cur!=NULL) { + if ((cur->sip[0]==dgw[0]) && + (cur->sip[1]==dgw[1]) && + (cur->sip[2]==dgw[2]) && + (cur->sip[3]==dgw[3])) { // entry found! + for (i=0; i<6; i++) { + device_list[devind].mac_gw[i] = cur->smac[i]; + } + } + cur=cur->next; + } + + // FINALLY: Copy findings in device_list + + if (device_list[devind].phy) { + for (i=0; i<4; i++) { + device_list[devind].net[i] = net[i]; + device_list[devind].mask[i] = mask[i]; + device_list[devind].ip_gw[i] = dgw[i]; + } + } + else { + for (i=0; i<4; i++) { + device_list[devind].net[i] = 0; + device_list[devind].mask[i] = 0; + device_list[devind].ip_gw[i] = 0; + } + } + + device_list[devind].index = index; + device_list[devind].mtu = mtu; + + return 0; +} + diff --git a/staging/mausezahn.c b/staging/mausezahn.c new file mode 100644 index 0000000..7f712b7 --- /dev/null +++ b/staging/mausezahn.c @@ -0,0 +1,1013 @@ +/* + * netsniff-ng - the packet sniffing beast + * Mausezahn, a fast versatile traffic generator + * Copyright 2008, 2009, 2010 Herbert Haas. + * Subject to the GPL, version 2. + */ + +#define _GNU_SOURCE +#include <libnet.h> +#include <pcap/pcap.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <limits.h> +#include <sys/time.h> +#include <time.h> +#include <signal.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <sys/ioctl.h> +#include <netinet/in.h> +#include <stdarg.h> + +#include "mz.h" +#include "cli.h" +#include "mops.h" +#include "llist.h" +#include "die.h" + +int verbose_level = 0; + +static const char *short_options = "46hqvVSxra:A:b:B:c:d:E:f:F:p:P:t:T:M:Q:X:"; + +static void signal_handler(int number) +{ + clean_up(number); +} + +void clean_up(int sig) +{ + int i; + struct arp_table_struct *cur, *next; + + if (!quiet) fprintf(stderr, "\nMausezahn cleans up...\n"); + + if (fp != NULL) { + verbose_l1(" close files (1) ...\n"); + + fflush(fp); + fclose(fp); + } + + if (fp2!=NULL) { + if (verbose) fprintf(stderr, " close files (2) ...\n"); + (void) fflush(fp2); + (void) fclose(fp2); + } + + // interactive mode? + if (mz_port) { + if (verbose) fprintf(stderr, " clear mops list...\n"); + mops_cleanup(mp_head); + if (verbose) fprintf(stderr, " clear automops list...\n"); + automops_cleanup(amp_head); + if (verbose) fprintf(stderr, " clear packet sequences...\n"); + mz_ll_delete_list(packet_sequences); + } + + for (i=0; i<device_list_entries; i++) { + if (device_list[i].p_arp!=NULL) { + pcap_close(device_list[i].p_arp); + fprintf(stderr, " stopped ARP process for device %s\n", device_list[i].dev); + } + if (device_list[i].arprx_thread!=0) { + pthread_cancel(device_list[i].arprx_thread); + if (verbose) + fprintf(stderr, " (ARP thread for device %s done)\n", device_list[i].dev); + } + + if (device_list[i].arp_table!=NULL) { + cur=device_list[i].arp_table; + while (cur!=NULL) { + next = cur->next; + if (cur!=NULL) free(cur); + cur=next; + } + } + + // close packet sockets + if (device_list[i].ps>=0) { + close(device_list[i].ps); + } + + } + + if (verbose) fprintf(stderr, "finished.\n"); + exit(sig); +} + + +static void help(void) +{ + printf("\nmausezahn %s, a fast versatile traffic generator\n", VERSION_STRING); + puts("http://www.netsniff-ng.org\n\n" + "Usage: mausezahn [options] [interface] <keyword>|<arg-string>|<hex-string>\n" + "Options:\n" + " -x <port> Interactive mode with telnet CLI, default port: 25542\n" + " -4 IPv4 mode (default)\n" + " -6 IPv6 mode\n" + " -c <count> Send packet count times, default:1, infinite:0\n" + " -d <delay> Apply delay between transmissions. The delay value can be\n" + " specified in usec (default, no additional unit needed), or in\n" + " msec (e.g. 100m or 100msec), or in seconds (e.g. 100s or 100sec)\n" + " -r Multiplies the specified delay with a random value\n" + " -p <length> Pad the raw frame to specified length (using random bytes)\n" + " -a <srcmac|keyword> Use specified source mac address, no matter what has\n" + " been specified with other arguments; keywords see below,\n" + " Default is own interface\n" + " -b <dstmac|keyword> Same with destination mac address; keywords:\n" + " rand Use a random MAC address\n" + " bc Use a broadcast MAC address\n" + " own Use own interface MAC address (default for source MAC)\n" + " stp Use IEEE 802.1d STP multicast address\n" + " cisco Use Cisco multicast address as used for CDP, VTP, or PVST+\n" + " -A <srcip> Use specified source IP address (default is own interface IP)\n" + " -B <dstip|dnsname> Send packet to specified destination IP or domain name\n" + " -P <ascii payload> Use the specified ASCII payload\n" + " -f <filename> Read the ASCII payload from a file\n" + " -F <filename> Read the hexadecimal payload from a file\n" + " -Q <[CoS:]vlan> Specify 802.1Q VLAN tag and optional Class of Service, you can\n" + " specify multiple 802.1Q VLAN tags (QinQ...) by separating them\n" + " via a comma or a period (e.g. '5:10,20,2:30')\n" + " -t <packet-type> Specify packet type for autobuild (you don't need to care for\n" + " encapsulations in lower layers, most packet types allow/require\n" + " additional packet-specific arguments in an <arg-string>;\n" + " Currently supported types: arp, bpdu, cdp, ip, icmp, udp, tcp,\n" + " dns, rtp, syslog, lldp and more;\n" + " For context-help use 'help' as <arg-string>!\n" + " -T <packet-type> Specify packet type for server mode, currently only rtp is supported;\n" + " Enter -T help or -T rtp help for further information\n" + " -M <MPLS-label> Insert a MPLS label, enter '-M help' for a syntax description\n" + " -V|VV|... Verbose and more verbose mode\n" + " -q Quiet mode, even omit 'important' standard short messages\n" + " -S Simulation mode: DOES NOT put anything on the wire, this is\n" + " typically combined with one of the verbose modes (v or V)\n" + " -v Show version\n" + " -h Print this help\n\n" + "Examples:\n" + " mausezahn -x 99\n" + " mausezahn -c 0 -d 2s -t bpdu conf\n" + " mausezahn -t cdp change -c 0\n" + " mausezahn -t syslog sev=3 -P \"You have been mausezahned.\" -A 10.1.1.109 -B 192.168.7.7\n" + " mausezahn eth0 -A rand -B 1.1.1.1 -c 0 -t tcp \"dp=1-1023, flags=syn\"\n\n" + "Note:\n" + " This tool is targeted for network developers! You should\n" + " be aware of what you are doing and what these options above\n" + " mean! Only use this tool in an isolated LAN that you own!\n\n" + "Please report bugs to <bugs@netsniff-ng.org>\n" + "Copyright (C) 2008-2010 Herbert Haas <herbert@perihel.at>,\n" + "Copyright (C) 2011 Daniel Borkmann <dborkma@tik.ee.ethz.ch>,\n" + "Swiss federal institute of technology (ETH Zurich)\n" + "License: GNU GPL version 2.0\n" + "This is free software: you are free to change and redistribute it.\n" + "There is NO WARRANTY, to the extent permitted by law.\n"); + die(); +} + +static void version(void) +{ + printf("\nmausezahn %s, a fast versatile traffic generator\n", VERSION_STRING); + puts("http://www.netsniff-ng.org\n\n" + "Please report bugs to <bugs@netsniff-ng.org>\n" + "Copyright (C) 2008-2010 Herbert Haas <herbert@perihel.at>,\n" + "Copyright (C) 2011 Daniel Borkmann <dborkma@tik.ee.ethz.ch>,\n" + "Swiss federal institute of technology (ETH Zurich)\n" + "License: GNU GPL version 2.0\n" + "This is free software: you are free to change and redistribute it.\n" + "There is NO WARRANTY, to the extent permitted by law.\n"); + die(); +} + +int reset() +{ + int i; + time_t t; + + // Determine platform type sizes: + MZ_SIZE_LONG_INT = sizeof(long int); + + mz_default_config_path[0] = 0x00; + mz_default_log_path[0] = 0x00; + + // Reset globals: + quiet = 0; + ipv6_mode = 0; + verbose = 0; + simulate = 0; + filename[0] = '\0'; + path[0] = '\0'; + gind=0; + gind_max = TIME_COUNT; + fp = NULL; + fp2 = NULL; + mz_port = 0; + mz_rand = 0; + mp_head = NULL; + + for (i=0;i<TIME_COUNT_MAX;i++) jitter[i] = 0; + + time0_flag = 0; // If set then time0 has valid data + sqnr0_flag = 0; // If set then sqnr_last and sqnr_next has valid data + rtp_log = 0; + mz_ssrc[0]=0; mz_ssrc[1]=0; mz_ssrc[2]=0; mz_ssrc[3]=0; + + // Reset mgmt parameters of TX: + tx.packet_mode = 1; // assume we don't care about L2 + tx.count = 1; + tx.delay = DEFAULT_DELAY; + tx.arg_string[0] = '\0'; + + // Reset Ethernet parameters of TX: + tx.eth_params_already_set = 0; + for (i=0; i<6; i++) tx.eth_dst[i] = 0xff; + for (i=0; i<6; i++) tx.eth_src[i] = 0; // TODO: Get own MAC !!! + tx.eth_dst_txt[0] = '\0'; + tx.eth_src_txt[0] = '\0'; + tx.eth_dst_rand = 0; + tx.eth_src_rand = 0; + + tx.eth_type = 0x800; + tx.eth_len = 0; + tx.eth_payload[0] = '\0'; + tx.eth_payload_s = 0; + tx.padding = 0; + + // Reset CDP parameters for TX: + tx.cdp_sum = 0; + tx.cdp_version = 0; + tx.cdp_ttl = 0; + tx.cdp_payload[0] = '\0'; + tx.cdp_payload_s = 0; + tx.cdp_tlv_id[0] = '\0'; + tx.cdp_tlv_id_len = 0; + + // Reset 802.1Q parameters of TX: + tx.dot1Q=0; + tx.dot1Q_txt[0] = '\0'; + + // ASCII Payload: + tx.ascii = 0; // 1 if specified + tx.ascii_payload[0]= '\0'; + + // HEX Payload: + tx.hex_payload_s = 0; + + // Reset MPLS parameters of TX: + tx.mpls = 0; + tx.mpls_txt[0] = '\0'; + tx.mpls_label = 0; + tx.mpls_exp = 0; + tx.mpls_bos = 1; + tx.mpls_ttl = 255; + tx.mpls_verbose_string[0] = '\0'; + + // Reset IP parameters of TX: + tx.ip_src_txt[0] = '\0'; + tx.ip_src_rand = 0; + tx.ip_dst_txt[0] = '\0'; + tx.ip_src_isrange = 0; + tx.ip_src_start = 0; + tx.ip_src_stop = 0; + + tx.ip_dst_start = 0; + tx.ip_dst_stop = 0; + tx.ip_dst_isrange = 0; + + tx.ip_len = 0; + tx.ip_payload[0]= '\0'; + tx.ip_payload_s = 0; + tx.ip_option[0]= '\0'; + tx.ip_option_s = 0; + + // Reset ICMP parameters: + tx.icmp_type=0; + tx.icmp_code=0; + tx.icmp_chksum=0; // 0=autofill + tx.icmp_ident=0x42; + tx.icmp_sqnr=0x1; + tx.icmp_payload_s=0; + + // Reset general L4 parameters: + tx.sp = 0; + tx.dp = 0; + tx.sp_start = 0; + tx.sp_stop = 0; + tx.dp_start = 0; + tx.dp_stop = 0; + tx.sp_isrange = 0; + tx.dp_isrange = 0; + + // Reset UDP parameters of TX: + + tx.udp_len = 0; // If set to zero then create_udp_packet will calculate it + tx.udp_sum = 0; + tx.udp_payload[0] = '\0'; + tx.udp_payload_s = 0; + + // Reset TCP parameters of TX: + + tx.tcp_seq = 42; + tx.tcp_seq_stop = 42; + tx.tcp_seq_delta = 0; // also used as 'isrange' meaning + tx.tcp_ack = 42; + tx.tcp_control = 0; + tx.tcp_win = 10000; + tx.tcp_sum = 0; + tx.tcp_urg = 0; + tx.tcp_len = 20; // Least size (TCP header only) + tx.tcp_payload[0] = '\0'; + tx.tcp_payload_s = 0; + + // Reset RTP parameters of TX: + tx.rtp_sqnr = 0; + tx.rtp_stmp = 0; + + // Initialize random generator + time(&t); + srand((unsigned int)t); + + // Reset device_list + for (i=0; i<MZ_MAX_DEVICES; i++) { + device_list[i].arprx_thread = 0; + device_list[i].p_arp = NULL; + device_list[i].arp_table = NULL; + device_list[i].ps=-1; + device_list[i].cli=0; + device_list[i].mgmt_only=0; + } + + return 0; +} + + + +// Purpose: Properly handle arguments and configure global structs (tx) +int getopts (int argc, char *argv[]) +{ + int i, c, rargs, RX=0, count_set=0, delay_set=0; + unsigned int time_factor; + char *packet_type=NULL, *mops_type=NULL; + char *dum; + unsigned char *dum1, *dum2; + + libnet_t *l; + char err_buf[LIBNET_ERRBUF_SIZE]; + struct libnet_ether_addr *mymac; + + FILE *afp; + char hexpld[MAX_PAYLOAD_SIZE*2]; + int hexpld_specified=0; + + opterr = 1; // let getopt print error message if necessary + + + while ((c = getopt(argc, argv, short_options)) != -1) + switch (c) { + case '4': + tx.eth_type = 0x0800; + ipv6_mode=0; + break; + case '6': + tx.eth_type = 0x86dd; + ipv6_mode=1; + break; + case 'h': + help(); + break; + case 'q': + quiet=1; + break; + case 'v': + version(); + break; + case 'V': + verbose++; + break; + case 'S': + simulate=1; + break; + case 'x': + mz_port = MZ_DEFAULT_PORT; + break; + case 'a': + strncpy (tx.eth_src_txt, optarg, 32); + tx.packet_mode = 0; + break; + case 'A': + strncpy (tx.ip_src_txt, optarg, sizeof(tx.ip_src_txt)); + break; + case 'b': + strncpy (tx.eth_dst_txt, optarg, 32); + tx.packet_mode = 0; + break; + case 'B': + strncpy (tx.ip_dst_txt, optarg, sizeof(tx.ip_dst_txt)); + break; + case 'c': + errno=0; + tx.count = strtol(optarg, (char **)NULL, 10); + if ((errno == ERANGE && (tx.count == LONG_MAX || tx.count == LONG_MIN)) + || (errno != 0 && tx.count == 0)) { + perror("strtol"); + return (-1); + } + if (tx.count<0) tx.count=1; //TODO: Allow count=0 which means infinity (need to update all send_functions) + count_set=1; + break; + case 'd': + errno=0; + // determine whether seconds or msecs are used + // default is usec!!! + time_factor=1; + if (exists(optarg,"s") || exists(optarg,"sec")) time_factor=1000000; + if (exists(optarg,"m") || exists(optarg,"msec")) time_factor=1000; + dum = strtok(optarg,"ms"); + tx.delay = strtol(dum, (char **)NULL, 10) * time_factor; + if ((errno == ERANGE && (tx.delay == LONG_MAX || tx.delay == LONG_MIN)) + || (errno != 0 && tx.delay == 0)) { + perror("strtol"); + return (-1); + } + if (tx.delay<0) tx.delay=0; // no delay + delay_set=1; + break; + case 'p': + errno=0; + tx.padding = strtol(optarg, (char **)NULL, 10); + if ((errno == ERANGE && (tx.padding == LONG_MAX || tx.padding == LONG_MIN)) + || (errno != 0 && tx.padding == 0)) { + perror("strtol"); + return (-1); + } + if (tx.padding>10000) { + fprintf(stderr, " Warning: Padding must not exceed 10000!\n"); + return -1; + } + break; + case 't': + packet_type = optarg; // analyzed below + break; + case 'X': + mops_type = optarg; // MOPS TRANSITION STRATEGY -- analyzed below + break; + case 'T': + packet_type = optarg; + RX = 1; + break; + case 'r': + mz_rand = 1; + break; + case 'M': + if (strncmp(optarg,"help",4)==0) { + (void) get_mpls_params("help "); + } + else { + strncpy (tx.mpls_txt, optarg, 128); + tx.eth_type = ETHERTYPE_MPLS; + tx.packet_mode = 0; + tx.mpls=1; + } + break; + case 'P': // ASCII payload + strncpy((char*)tx.ascii_payload, optarg, MAX_PAYLOAD_SIZE); + tx.ascii = 1; + break; + case 'f': // ASCII payload in FILE + afp = fopen(optarg, "r"); + if (fgets((char*)tx.ascii_payload, MAX_PAYLOAD_SIZE, afp) == NULL) + fprintf(stderr, " mz/getopts: File empty?\n"); + fclose(afp); + tx.ascii = 1; + break; + case 'F': // HEX payload in FILE + afp = fopen(optarg, "r"); + i=0; + while ( (hexpld[i]=fgetc(afp))!=EOF ) { + if (isspace(hexpld[i])) { + hexpld[i]=':'; + } + i++; + } + hexpld[i]='\0'; + fclose(afp); + hexpld_specified=1; + break; + case 'Q': // VLAN TAG + if (strncmp(optarg,"help",4)==0) { + print_dot1Q_help(); // ugly but most simple and safe solution + } + else { + strncpy (tx.dot1Q_txt, optarg, 32); + tx.dot1Q=1; + // determine number of VLAN tags + for (i=0; i<strlen(tx.dot1Q_txt); i++) { + if (tx.dot1Q_txt[i]==',') tx.dot1Q++; + } + tx.packet_mode = 0; + } + break; + case '?': + if ((optopt == 'a') || (optopt == 'b') || (optopt = 'c') || + (optopt == 'd') || (optopt == 'f') || (optopt = 'p') || + (optopt == 't') || (optopt == 'm')) + fprintf (stderr, " mz/getopts: Option -%c requires an argument.\n", optopt); + else if (isprint (optopt)) + fprintf (stderr, " mz/getopts: Unknown option -%c'.\n", optopt); + else + fprintf (stderr, " mz/getopts: Unknown option character \\x%x'.\n", optopt); + return 1; + default: + fprintf (stderr," mz/getopts: Could not handle arguments properly!\n"); + return 1; + } + + // ******************************************** + // Handle additional arguments + // ******************************************** + // + // Greeting text + if (verbose) { + fprintf(stderr,"\n" + MAUSEZAHN_VERSION + "\n" + "Use at your own risk and responsibility!\n" + "-- Verbose mode --\n" + "\n"); + } + + if (argc<2) { + help(); + } + + if ((rargs=argc-optind)>2) { // number of remaining arguments + fprintf(stderr," mz/getopts: Too many arguments!\n"); + return -1; + } + + + // There can be 0-2 additional arguments + switch (rargs) { + case 0: + if (lookupdev()) { // no device found + if (verbose) fprintf(stderr, " mz: no active interfaces found!\n"); + strcpy(tx.device, "lo"); + } + if (verbose) // device found + fprintf(stderr," mz: device not given, will use %s\n",tx.device); + break; + case 1: // arg_string OR device given => find out! + if ( (strncmp(argv[optind],"eth",3)==0) + || (strncmp(argv[optind],"ath",3)==0) + || ((strncmp(argv[optind],"lo",2)==0)&&(strncmp(argv[optind],"log",3)!=0)) + || (strncmp(argv[optind],"vmnet",5)==0) + || (strncmp(argv[optind],"wifi",4)==0) ) { + // device has been specified! + strncpy (tx.device, argv[optind], 16); + } + else { /// arg_string given => no device has been specified -- let's find one! + strncpy (tx.arg_string, argv[optind], MAX_PAYLOAD_SIZE); + if (lookupdev()) { // no device found + if (verbose) fprintf(stderr, " mz: no active interfaces found!\n"); + strcpy(tx.device, "lo"); + } + if (verbose) + fprintf(stderr," mz: device not given, will use %s\n",tx.device); + } + break; + case 2: // both device and arg_string given + strncpy (tx.device, argv[optind], 16); + strncpy (tx.arg_string, argv[optind+1], MAX_PAYLOAD_SIZE); + break; + default: + fprintf(stderr," mz/getopts: Unknown argument problem!\n"); + return 1; + } + + if (hexpld_specified) { + strcat(tx.arg_string, ",p="); + strcat(tx.arg_string, hexpld); + } + + + ////////////////////////////////////////////////////////////////////////// + // + // Initialize MAC and IP Addresses. + // + // - tx.eth_src = own interface MAC + // - tx.ip_src = own interface IP or user specified + // - tx.ip_dst = 255.255.255.255 or user specified (can be a range) + // - tx.ip_src_rand ... is set if needed. + // + + // Get own device MAC address: + // Don't open context if only a help text is requested + if (getarg(tx.arg_string,"help", NULL)!=1) { + l = libnet_init (LIBNET_LINK_ADV, tx.device, err_buf ); + if (l == NULL) { + fprintf(stderr, " mz/getopts: libnet_init() failed (%s)", err_buf); + return -1; + } + mymac = libnet_get_hwaddr(l); + for (i=0; i<6; i++) { + tx.eth_src[i] = mymac->ether_addr_octet[i]; + tx.eth_mac_own[i] = mymac->ether_addr_octet[i]; + } + + // Set source IP address: + if (strlen(tx.ip_src_txt)) { // option -A has been specified + if (mz_strcmp(tx.ip_src_txt, "bcast", 2)==0) { + tx.ip_src = libnet_name2addr4 (l, "255.255.255.255", LIBNET_DONT_RESOLVE); + } else if (strcmp(tx.ip_src_txt, "rand") == 0) { + tx.ip_src_rand = 1; + tx.ip_src_h = (u_int32_t) ( ((float) rand()/RAND_MAX)*0xE0000000); //this is 224.0.0.0 + } + else if (get_ip_range_src(tx.ip_src_txt)) { // returns 1 when no range has been specified + // name2addr4 accepts a DOTTED DECIMAL ADDRESS or a FQDN: + if (ipv6_mode) + tx.ip6_src = libnet_name2addr6 (l, tx.ip_src_txt, LIBNET_RESOLVE); + else + tx.ip_src = libnet_name2addr4 (l, tx.ip_src_txt, LIBNET_RESOLVE); + } + } + else { // no source IP specified: by default use own IP address + if (ipv6_mode) { + tx.ip6_src = libnet_get_ipaddr6(l); + if (strncmp((char*)&tx.ip6_src,(char*)&in6addr_error,sizeof(in6addr_error))==0) + printf("Failed to set source IPv6 address: %s", l->err_buf); + } + else + tx.ip_src = libnet_get_ipaddr4(l); + } + + // Set destination IP address: + if (strlen(tx.ip_dst_txt)) { // option -B has been specified + if (mz_strcmp(tx.ip_dst_txt, "rand", 2)==0) { + fprintf(stderr, "Option -B does not support random destination IP addresses currently.\n"); + return 1; + } + + if (mz_strcmp(tx.ip_dst_txt, "bcast", 2)==0) { + tx.ip_dst = libnet_name2addr4 (l, "255.255.255.255", LIBNET_DONT_RESOLVE); + } else if (get_ip_range_dst(tx.ip_dst_txt)) { // returns 1 when no range has been specified + // name2addr4 accepts a DOTTED DECIMAL ADDRESS or a FQDN: + if (ipv6_mode) + tx.ip6_dst = libnet_name2addr6 (l, tx.ip_dst_txt, LIBNET_RESOLVE); + else + tx.ip_dst = libnet_name2addr4 (l, tx.ip_dst_txt, LIBNET_RESOLVE); + } + } + else { // no destination IP specified: by default use broadcast + tx.ip_dst = libnet_name2addr4 (l, "255.255.255.255", LIBNET_DONT_RESOLVE); + } + + // Initialize tx.ip_src_h and tx.ip_dst_h which are used by 'print_frame_details()' + // in verbose mode. See 'modifications.c'. + + if (tx.ip_src_rand) { // ip_src_h already given, convert to ip_src + dum1 = (unsigned char*) &tx.ip_src_h; + dum2 = (unsigned char*) &tx.ip_src; + } + else { // ip_src already given, convert to ip_src_h + dum1 = (unsigned char*) &tx.ip_src; + dum2 = (unsigned char*) &tx.ip_src_h; + } + + *dum2 = *(dum1+3); + dum2++; + *dum2 = *(dum1+2); + dum2++; + *dum2 = *(dum1+1); + dum2++; + *dum2 = *dum1; + + dum1 = (unsigned char*) &tx.ip_dst; + dum2 = (unsigned char*) &tx.ip_dst_h; + + *dum2 = *(dum1+3); + dum2++; + *dum2 = *(dum1+2); + dum2++; + *dum2 = *(dum1+1); + dum2++; + *dum2 = *dum1; + + libnet_destroy(l); + } + + // + // END OF ADDRESS INITIALIZATION + // + ////////////////////////////////////////////////////////////////////////// + + + ////// retrieve interface parameters /////// + + for (i=0; i<device_list_entries; i++) { + get_dev_params(device_list[i].dev); + } + + + ////////////////////////////////////////////////////////////////////////// + // + // Mausezahn CLI desired? + if (mz_port) { + // has port number been specified? + if (strlen(tx.arg_string)) { + mz_port = (int) str2int (tx.arg_string); + } + + if (!quiet) { + fprintf(stderr, "Mausezahn accepts incoming Telnet connections on port %i.\n", mz_port); + } + + mz_cli_init(); + cli(); + } + + ////////////////////////////////////////////////////////////////////////// + // + // Mode decision + // + // Consider -t and -m option (used exclusively) + // -t => special packet types, stateless + // + // If -t not present then evaluate arg_string which must + // contain a byte-string in hexadecimal notation. + // + // + + // ***** NEW: MOPS TRANSITION STRATEGY ***** + if (mops_type != NULL) { + + if (mz_strcmp(mops_type,"lldp",4)==0) { + mops_direct(tx.device, MOPS_LLDP, tx.arg_string); + } + } + + + if (packet_type == NULL) { // raw hex string given + mode = BYTE_STREAM; + } + else if (strcmp(packet_type,"arp")==0) { + mode = ARP; + } + else if (strcmp(packet_type,"bpdu")==0) { + mode = BPDU; + } + else if (strcmp(packet_type,"ip")==0) { + mode = IP; + } + else if (strcmp(packet_type,"udp")==0) { + mode = UDP; + } + else if (strcmp(packet_type,"icmp")==0) { + mode = ICMP; + } + else if (strcmp(packet_type,"icmp6")==0) { + mode = ICMP6; + } + else if (strcmp(packet_type,"tcp")==0) { + mode = TCP; + } + else if (strcmp(packet_type,"dns")==0) { + mode = DNS; + } + else if (strcmp(packet_type,"cdp")==0) { + mode = CDP; + } + else if (strcmp(packet_type,"syslog")==0) { + mode = SYSLOG; + } + else if (strcmp(packet_type,"lldp")==0) { + mode = LLDP; + tx.packet_mode=0; // create whole frame by ourself + } + else if (strcmp(packet_type,"rtp")==0) { + if (RX) { + mode = RX_RTP; + } + else { + mode = RTP; + if (!count_set) tx.count = 0; + if (!delay_set) tx.delay = 20000; // 20 msec inter-packet delay for RTP + } + } + else if (strcmp(packet_type,"help")==0) { + fprintf(stderr, "\n" + MAUSEZAHN_VERSION + "\n" + "| The following packet types are currently implemented:\n" + "|\n" + "| arp ... sends ARP packets\n" + "| bpdu ... sends BPDU packets (STP or PVST+)\n" + "| cdp ... sends CDP messages\n" + "| ip ... sends IPv4 packets\n" + "| udp ... sends UDP datagrams\n" + "| tcp ... sends TCP segments\n" + "| icmp ... sends ICMP messages\n" + "| dns ... sends DNS messages\n" + "| rtp ... sends RTP datagrams\n" + "| syslog ... sends Syslog messages\n" + "|\n" + "| Of course you can build any other packet type 'manually' using the direct layer 2 mode.\n" + "| FYI: The interactive mode supports additional protocols. (Try mz -x <port>)\n" + "\n" + ); + exit(1); + } + else { + fprintf(stderr, " mz: you must specify a valid packet type!\n"); + } + + + ////////////////////////////////////////////////////////////////////////// + + // TODO: Implement macro support + // Check macro types here + + return 0; +} + +int main(int argc, char **argv) +{ + // These handles are only used when creating L3 and above packets. + libnet_t *l; // the context + libnet_ptag_t t2=0, t3=0, t4=0; // handles to layers + double cpu_time_used; + + reset(); + + if ( getopts(argc, argv) ) + { + (void) fprintf(stderr, " Invalid command line parameters!\n"); + help(); + } + + // Check whether hires timers are supported or not: + (void) check_timer(); + + signal(SIGINT, signal_handler); // to close all file pointers etc upon SIGINT + + switch (mode) + { + case BYTE_STREAM: + send_eth(); + break; + + case ARP: + (void) send_arp(); + break; + + case BPDU: + (void) send_bpdu(); + break; + + case CDP: + (void) send_cdp(); + break; + + case IP: // From now on a new much more modular method is used: + l = get_link_context(); + t3 = create_ip_packet(l); // t3 can be used for later header changes + if (!quiet) complexity(); + if (tx.packet_mode==0) // Ethernet manipulation features does NOT use ARP to determine eth_dst + t2 = create_eth_frame(l, t3, t4); // t2 can be used for later header changes + else + send_frame (l, t3, t4); // NOTE: send_frame also destroys context finaly + break; + + case ICMP: + tx.ip_proto = 1; + l = get_link_context(); + t4 = create_icmp_packet(l); // t4 can be used for later header changes + t3 = create_ip_packet(l); // t3 can be used for later header changes + if (!quiet) complexity(); + if (tx.packet_mode==0) // Ethernet manipulation features does NOT use ARP to determine eth_dst + t2 = create_eth_frame(l, t3, t4); // t2 can be used for later header changes + else + send_frame (l, t3, t4); // NOTE: send_frame also destroys context finaly + break; + + case ICMP6: + tx.ip_proto = 58; + l = get_link_context(); + t4 = create_icmp6_packet(l); // t4 can be used for later header changes + t3 = create_ip_packet(l); // t3 can be used for later header changes + if (ipv6_mode) + update_ISUM(l, t4); + if (!quiet) complexity(); + if (tx.packet_mode==0) // Ethernet manipulation features does NOT use ARP to determine eth_dst + t2 = create_eth_frame(l, t3, t4); // t2 can be used for later header changes + else + send_frame (l, t3, t4); // NOTE: send_frame also destroys context finaly + break; + + case UDP: + tx.ip_proto = 17; + l = get_link_context(); + t4 = create_udp_packet(l); // t4 can be used for later header changes + t3 = create_ip_packet(l); // t3 can be used for later header changes + if (ipv6_mode) + update_USUM(l, t4); + if (!quiet) complexity(); + if (tx.packet_mode==0) // Ethernet manipulation features does NOT use ARP to determine eth_dst + t2 = create_eth_frame(l, t3, t4); // t2 can be used for later header changes + else + send_frame (l, t3, t4); // NOTE: send_frame also destroys context finaly + break; + + case TCP: + tx.ip_proto = 6; + l = get_link_context(); + t4 = create_tcp_packet(l); // t4 can be used for later header changes + t3 = create_ip_packet(l); // t3 can be used for later header changes + if (ipv6_mode) + update_TSUM(l, t4); + if (!quiet) complexity(); + if (tx.packet_mode==0) // Ethernet manipulation features does NOT use ARP to determine eth_dst + t2 = create_eth_frame(l, t3, t4); // t2 can be used for later header changes + else + send_frame (l, t3, t4); // NOTE: send_frame also destroys context finaly + break; + + case DNS: + tx.ip_proto = 17; + l = get_link_context(); + (void) create_dns_packet(); + t4 = create_udp_packet(l); // t4 can be used for later header changes + t3 = create_ip_packet(l); // t3 can be used for later header changes + if (!quiet) complexity(); + if (tx.packet_mode==0) // Ethernet manipulation features does NOT use ARP to determine eth_dst + t2 = create_eth_frame(l, t3, t4); // t2 can be used for later header changes + else + send_frame (l, t3, t4); // NOTE: send_frame also destroys context finaly + break; + + case RTP: + tx.ip_proto = 17; + l = get_link_context(); + if (!quiet) fprintf(stderr, " mz: RTP mode! (count=%u, delay=%u usec)\n\n", tx.count, tx.delay); + (void) create_rtp_packet(); + t4 = create_udp_packet(l); // t4 can be used for later header changes + t3 = create_ip_packet(l); // t3 can be used for later header changes + if (!quiet) complexity(); + if (tx.packet_mode==0) // Ethernet manipulation features does NOT use ARP to determine eth_dst + t2 = create_eth_frame(l, t3, t4); // t2 can be used for later header changes + else + send_frame (l, t3, t4); // NOTE: send_frame also destroys context finaly + break; + + case RX_RTP: // Receive RTP packets + rcv_rtp_init(); + rcv_rtp(); + break; + + case SYSLOG: + tx.ip_proto = 17; + l = get_link_context(); + (void) create_syslog_packet(); + t4 = create_udp_packet(l); // t4 can be used for later header changes + t3 = create_ip_packet(l); // t3 can be used for later header changes + if (!quiet) complexity(); + + if (tx.packet_mode==0) // Ethernet manipulation features does NOT use ARP to determine eth_dst + t2 = create_eth_frame(l, t3, t4); // t2 can be used for later header changes + else + send_frame (l, t3, t4); // NOTE: send_frame also destroys context finaly + break; + + case LLDP: // start with a new concept here + //l = get_link_context(); + //(void) create_lldp_packet(); + // // // printf("SIZE=%lu\n",sizeof(struct tx_struct)); + fprintf(stderr, "LLDP is currently only supported via the interactive mode\n"); + exit(1); + break; + + + default: + (void) fprintf(stderr," mz/main: unknown mode! Stop.\n"); + return (1); + } + + if (!quiet) + { + mz_stop = clock(); + cpu_time_used = ((double) (mz_stop - mz_start)) / CLOCKS_PER_SEC; + if (cpu_time_used > 0) + { + total_d /= cpu_time_used; + fprintf(stderr, "%.2f seconds (%.Lf packets per second)\n",cpu_time_used,total_d); + } + else + { + fprintf(stderr, "\n"); + } + } + + return(0); +} diff --git a/staging/modifications.c b/staging/modifications.c new file mode 100644 index 0000000..3dc2abf --- /dev/null +++ b/staging/modifications.c @@ -0,0 +1,698 @@ +/* + * 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 + * +*/ + + + + +// *************************************************************************** +// +// This sections contains functions to manipulate headers of +// Eth, MPLS, 802.1Q, IP, UDP, and TCP: +// +// int update_Eth_SA (libnet_t *l, libnet_ptag_t t) +// int update_IP_SA (libnet_t *l, libnet_ptag_t t) +// int update_IP_DA (libnet_t *l, libnet_ptag_t t) +// int update_DPORT (libnet_t *l, libnet_ptag_t t) +// int update_SPORT (libnet_t *l, libnet_ptag_t t) +// int update_TCP_SQNR (libnet_t *l, libnet_ptag_t t) +// +// and finally: +// +// int print_frame_details() +// +// *************************************************************************** + +#include "mz.h" +#include "mops.h" + +/////////////////////////////////////////////////////////////////////////// +// Applies another random Ethernet source address to a given Ethernet-PTAG. +// (The calling function should check 'tx.eth_src_rand' whether the SA +// should be randomized.) +// +int update_Eth_SA(libnet_t *l, libnet_ptag_t t) +{ + tx.eth_src[0] = (u_int8_t) ( ((float) rand()/RAND_MAX)*256) & 0xFE; // keeps bcast-bit zero + tx.eth_src[1] = (u_int8_t) ( ((float) rand()/RAND_MAX)*256); + tx.eth_src[2] = (u_int8_t) ( ((float) rand()/RAND_MAX)*256); + tx.eth_src[3] = (u_int8_t) ( ((float) rand()/RAND_MAX)*256); + tx.eth_src[4] = (u_int8_t) ( ((float) rand()/RAND_MAX)*256); + tx.eth_src[5] = (u_int8_t) ( ((float) rand()/RAND_MAX)*256); + + t = libnet_build_ethernet (tx.eth_dst, + tx.eth_src, + tx.eth_type, + NULL, // the payload + 0, + l, + t); + + if (t == -1) + { + fprintf(stderr, " mz/update_Eth_SA: Can't build Ethernet header: %s\n", + libnet_geterror(l)); + exit(EXIT_FAILURE); + } + + return 0; +} + + +// Update official timestamp, own timestamp and sequence number in the RTP header. +// The actual RTP message is stored in tx.udp_payload. +int update_RTP(libnet_t *l, libnet_ptag_t t) +{ + u_int8_t *ptr; + struct mz_timestamp ts; + + tx.rtp_sqnr++; + tx.rtp_stmp+=160; // TODO: different values for different codecs + + // update SQNR + ptr = (u_int8_t*) &tx.rtp_sqnr; + tx.udp_payload[2] = *(ptr+1); + tx.udp_payload[3] = *ptr; + + // update official timestamp + ptr = (u_int8_t*) &tx.rtp_stmp; + tx.udp_payload[4] = *(ptr+3); + tx.udp_payload[5] = *(ptr+2); + tx.udp_payload[6] = *(ptr+1); + tx.udp_payload[7] = *ptr; + + + // update own timestamp + getcurtime(&ts); // Now add TX timestamp: + mops_hton4 ((u_int32_t*) &ts.sec, &tx.udp_payload[16]); + mops_hton4 ((u_int32_t*) &ts.nsec, &tx.udp_payload[20]); + + t = libnet_build_udp(tx.sp, + tx.dp, + tx.udp_len, + tx.udp_sum, + tx.udp_payload, + tx.udp_payload_s, + l, + t); + + if (t == -1) { + fprintf(stderr," mz/send_frame: RTP header update failed!\n"); + exit (1); + } + return 0; +} + + +/////////////////////////////////////////////////////////////////////////// +// Applies another SOURCE IP address, +// - either a random one (tx.ip_src_rand==1) +// - or from a specified range (tx.ip_src_isrange==1) +// to a given IP-PTAG. +// +// Note: tx.ip_src MUST be already initialized with tx.ip_src_start. +// This is done by 'get_ip_range_src()' in tools.c. +// +// +// RETURNS '1' if tx.ip_src restarts +// +int update_IP_SA (libnet_t *l, libnet_ptag_t t) +{ + u_int8_t *x, *y; + int i=0; + + if (tx.ip_src_rand) + { + tx.ip_src_h = (u_int32_t) ( ((float) rand()/RAND_MAX)*0xE0000000); //this is 224.0.0.0 + i=1; + } + else if (tx.ip_src_isrange) + { + tx.ip_src_h++; + if (tx.ip_src_h > tx.ip_src_stop) // reached the end of the range => restart! + { + tx.ip_src_h = tx.ip_src_start; + i=1; + } + } + + // Now convert "tx.ip_src_h" into "tx.ip_src" which is in 'Network Byte Order': + x = (unsigned char*) &tx.ip_src_h; + y = (unsigned char*) &tx.ip_src; + + *y = *(x+3); + y++; + *y = *(x+2); + y++; + *y = *(x+1); + y++; + *y = *x; + + // TODO: Omit certain IP addresses: + // E.g. if (rand_ip == tx.ip_src) goto rand_again; // never use true interface IP + // TODO: Check other address exceptions ... + + t = libnet_build_ipv4 (tx.ip_len, + tx.ip_tos, + tx.ip_id, + tx.ip_frag, + tx.ip_ttl, + tx.ip_proto, + tx.ip_sum, + tx.ip_src, // possibly now random + tx.ip_dst, + (mode==IP) ? (tx.ip_payload_s) ? tx.ip_payload : NULL : NULL, // if e.g. mode=UDP ignore payload argument + (mode==IP) ? tx.ip_payload_s : 0, + l, + t); + + if (t == -1) + { + fprintf(stderr," mz/update_IP_SA: IP address manipulation failed!\n"); + exit (1); + } + + return i; +} + + + + +///////////////////////////////////////////////////////////////////////////////////////// +// Applies another DESTINATION IP address from a specified range (tx.ip_dst_isrange==1) +// to a given IP-PTAG. +// +// Note: tx.ip_dst MUST be already initialized with tx.ip_dst_start. +// tx.ip_dst_h 'mirrors' tx.ip_dst +// (i. e. tx.ip_dst_h is NOT in network byte order => easy to count) +// This is done by 'get_ip_range_dst()' in tools.c. +// +// RETURN VALUE: '1' if tx.ip_dst restarts +// +int update_IP_DA(libnet_t *l, libnet_ptag_t t) +{ + u_int8_t *x, *y; + int i=0; + + + if (tx.ip_dst_isrange) + { + tx.ip_dst_h++; + if (tx.ip_dst_h > tx.ip_dst_stop) // we reached the end of the range => restart! + { + tx.ip_dst_h = tx.ip_dst_start; + i=1; + } + } + + + // Now convert "tx.ip_dst_h" into "tx.ip_dst" which is in 'Network Byte Order': + + x = (unsigned char*) &tx.ip_dst_h; + y = (unsigned char*) &tx.ip_dst; + + *y = *(x+3); + y++; + *y = *(x+2); + y++; + *y = *(x+1); + y++; + *y = *x; + + + // TODO: Omit certain IP addresses: + // E.g. if (rand_ip == tx.ip_src) goto rand_again; // never use true interface IP + // TODO: Check other address exceptions ... + + t = libnet_build_ipv4 (tx.ip_len, + tx.ip_tos, + tx.ip_id, + tx.ip_frag, + tx.ip_ttl, + tx.ip_proto, + tx.ip_sum, + tx.ip_src, + tx.ip_dst, + (mode==IP) ? (tx.ip_payload_s) ? tx.ip_payload : NULL : NULL, // if e.g. mode=UDP ignore payload argument + (mode==IP) ? tx.ip_payload_s : 0, + l, + t); + + if (t == -1) + { + fprintf(stderr," mz/update_IP_DA: IP address manipulation failed!\n"); + exit (1); + } + + return i; +} + + + + +/////////////////////////////////////////////////////////////////////////////////////// +// +// Applies another DESTINATION PORT from a specified range to a given UDP- or TCP-PTAG. +// +// Note: tx.dp MUST be already initialized with tx.dp_start +// This is done by 'get_port_range()' in tools.c. +// +// RETURN VALUE: '1' if tx.dp restarts +// +int update_DPORT(libnet_t *l, libnet_ptag_t t) +{ + // u_int32_t DP; + int i=0; + + // DP = (u_int32_t) tx.dp; + // DP++; + tx.dp++; + + + // Exceeded range => restart: + if ((tx.dp > tx.dp_stop) || // we exceeded the end of the range + (tx.dp == 65535) ) // or exceeded the 16-bit range + { + tx.dp = tx.dp_start; + i=1; + } + + + if (mode==UDP) + { + t = libnet_build_udp(tx.sp, + tx.dp, + tx.udp_len, + tx.udp_sum, + (tx.udp_payload_s) ? tx.udp_payload : NULL, + tx.udp_payload_s, + l, + t); + + if (t == -1) + { + fprintf(stderr," mz/send_frame: UDP header manipulation failed!\n"); + exit (1); + } + } + else // TCP + { + t = libnet_build_tcp (tx.sp, + tx.dp, + tx.tcp_seq, + tx.tcp_ack, + tx.tcp_control, + tx.tcp_win, + tx.tcp_sum, + tx.tcp_urg, + tx.tcp_len, + (tx.tcp_payload_s) ? tx.tcp_payload : NULL, + tx.tcp_payload_s, + l, + t); + + if (t == -1) + { + fprintf(stderr, " mz/update_DPORT: Can't build TCP header: %s\n", libnet_geterror(l)); + exit (0); + } + } + + return i; +} + + +/////////////////////////////////////////////////////////////////////////////////// +// +// Applies another SOURCE PORT from a specified range to a given UDP- or TCP-PTAG. +// +// Note: tx.sp MUST be already initialized with tx.sp_start +// This is done by 'get_port_range()' in tools.c. +// +// RETURN VALUE: '1' if tx.sp restarts +// +int update_SPORT(libnet_t *l, libnet_ptag_t t) +{ + +// u_int32_t SP; + int i=0; + +// SP = (u_int32_t) tx.sp; +// SP++; + tx.sp++; + + + // Exceeded range => restart: + if ((tx.sp > tx.sp_stop) || // we exceeded the end of the range + (tx.sp == 65535) ) // or exceeded the 16-bit range + { + tx.sp = tx.sp_start; + i=1; + } + + if (mode==UDP) + { + t = libnet_build_udp(tx.sp, + tx.dp, + tx.udp_len, + tx.udp_sum, + (tx.udp_payload_s) ? tx.udp_payload : NULL, + tx.udp_payload_s, + l, + t); + + if (t == -1) + { + fprintf(stderr," mz/send_frame: UDP header manipulation failed!\n"); + exit (1); + } + } + else // TCP + { + t = libnet_build_tcp (tx.sp, + tx.dp, + tx.tcp_seq, + tx.tcp_ack, + tx.tcp_control, + tx.tcp_win, + tx.tcp_sum, + tx.tcp_urg, + tx.tcp_len, + (tx.tcp_payload_s) ? tx.tcp_payload : NULL, + tx.tcp_payload_s, + l, + t); + + if (t == -1) + { + fprintf(stderr, " mz/update_DPORT: Can't build TCP header: %s\n", libnet_geterror(l)); + exit (0); + } + } + + return i; +} + +#define LIBNET_CKSUM_CARRY(x) \ + (x = (x >> 16) + (x & 0xffff), (~(x + (x >> 16)) & 0xffff)) + +int update_USUM(libnet_t *l, libnet_ptag_t t) +{ + int sum = 0; + unsigned int tmp; + + if (tx.udp_sum != 0) + return 0; + + sum += libnet_in_cksum((u_int16_t *) &tx.ip6_src, 16); + if (tx.ip_option_s && tx.ip6_segs) + sum += libnet_in_cksum((u_int16_t *) &tx.ip_option[tx.ip_option_s - 16], 16); // Use last IP address + else + sum += libnet_in_cksum((u_int16_t *) &tx.ip6_dst, 16); + + tmp = htonl(tx.udp_len); + sum += libnet_in_cksum((u_int16_t *) &tmp, 4); + tmp = htonl(IPPROTO_UDP); + sum += libnet_in_cksum((u_int16_t *) &tmp, 4); + + tmp = ((htons(tx.sp) << 16) + htons(tx.dp)); + sum += libnet_in_cksum((u_int16_t *) &tmp, 4); + + tmp = htons(tx.udp_len) << 16; + sum += libnet_in_cksum((u_int16_t *) &tmp, 4); + + if (tx.udp_payload_s) + sum += libnet_in_cksum((u_int16_t *) tx.udp_payload, tx.udp_payload_s); + + tx.udp_sum = ntohs(LIBNET_CKSUM_CARRY(sum)); + + t = libnet_build_udp(tx.sp, + tx.dp, + tx.udp_len, + tx.udp_sum, + tx.udp_payload_s ? tx.udp_payload : NULL, + tx.udp_payload_s, + l, + t); + return t; +} + +int update_TSUM(libnet_t *l, libnet_ptag_t t) +{ + int sum = 0; + unsigned int tmp; + + if (tx.tcp_sum != 0) + return 0; + + sum += libnet_in_cksum((u_int16_t *) &tx.ip6_src, 16); + if (tx.ip_option_s && tx.ip6_segs) + sum += libnet_in_cksum((u_int16_t *) &tx.ip_option[tx.ip_option_s - 16], 16); // Use last IP address + else + sum += libnet_in_cksum((u_int16_t *) &tx.ip6_dst, 16); + + tmp = htonl(tx.tcp_len); + sum += libnet_in_cksum((u_int16_t *) &tmp, 4); + tmp = htonl(IPPROTO_TCP); + sum += libnet_in_cksum((u_int16_t *) &tmp, 4); + + tmp = ((htons(tx.sp) << 16) + htons(tx.dp)); + sum += libnet_in_cksum((u_int16_t *) &tmp, 4); + + tmp = htonl(tx.tcp_seq); + sum += libnet_in_cksum((u_int16_t *) &tmp, 4); + tmp = htonl(tx.tcp_ack); + sum += libnet_in_cksum((u_int16_t *) &tmp, 4); + + tmp = ((ntohs(((tx.tcp_offset) << 12) + tx.tcp_control) << 16) + htons(tx.tcp_win)); + sum += libnet_in_cksum((u_int16_t *) &tmp, 4); + + tmp = htonl(tx.tcp_urg); + sum += libnet_in_cksum((u_int16_t *) &tmp, 4); + + sum += tx.tcp_sum_part; + + if (tx.tcp_payload_s) + sum += libnet_in_cksum((u_int16_t *) tx.tcp_payload, tx.tcp_payload_s); + + tx.tcp_sum = ntohs(LIBNET_CKSUM_CARRY(sum)); + + t = libnet_build_tcp (tx.sp, + tx.dp, + tx.tcp_seq, + tx.tcp_ack, + tx.tcp_control, + tx.tcp_win, + tx.tcp_sum, + tx.tcp_urg, + tx.tcp_len, + tx.tcp_payload_s ? tx.tcp_payload : NULL, + tx.tcp_payload_s, + l, + t); + + return t; +} + +int update_ISUM(libnet_t *l, libnet_ptag_t t) +{ + int sum = 0; + unsigned int tmp; + + if (tx.icmp_chksum != 0) + return 0; + + sum += libnet_in_cksum((u_int16_t *) &tx.ip6_src, 16); + if (tx.ip_option_s && tx.ip6_segs) + sum += libnet_in_cksum((u_int16_t *) &tx.ip_option[tx.ip_option_s - 16], 16); // Use last IP address + else + sum += libnet_in_cksum((u_int16_t *) &tx.ip6_dst, 16); + + tmp = htonl(LIBNET_ICMPV6_H + tx.icmp_payload_s); + sum += libnet_in_cksum((u_int16_t *) &tmp, 4); + tmp = htonl(IPPROTO_ICMP6); + sum += libnet_in_cksum((u_int16_t *) &tmp, 4); + + tmp = htonl(((tx.icmp_type << 8) + tx.icmp_code)); + sum += libnet_in_cksum((u_int16_t *) &tmp, 4); + + if (tx.icmp_payload_s) + sum += libnet_in_cksum((u_int16_t *) tx.icmp_payload, tx.icmp_payload_s); + + tx.icmp_chksum = ntohs(LIBNET_CKSUM_CARRY(sum)); + + t = libnet_build_icmpv4_echo (tx.icmp_type, + tx.icmp_code, + tx.icmp_chksum, + tx.icmp_ident, + tx.icmp_sqnr, + tx.icmp_payload_s ? tx.icmp_payload : NULL, + tx.icmp_payload_s, + l, + t); + + return t; +} + +/////////////////////////////////////////////////////////////////////// +// +// Applies another TCP SQNR from a specified range to a given TCP-PTAG +// +// RETURN VALUE: '1' if tx.txp_seq restarts +// +int update_TCP_SQNR(libnet_t *l, libnet_ptag_t t) +{ + + u_int32_t diff; + int i=0; + + tx.tcp_seq += tx.tcp_seq_delta; + diff = tx.tcp_seq_stop - tx.tcp_seq_start; + + if (diff < tx.tcp_seq_stop) // start < stop + { + if (tx.tcp_seq > tx.tcp_seq_stop) + { + tx.tcp_seq = tx.tcp_seq_start; + i=1; + } + } + else // stop < start + { + if ( (tx.tcp_seq<tx.tcp_seq_start) && + (tx.tcp_seq>tx.tcp_seq_stop) ) + { + tx.tcp_seq = tx.tcp_seq_start; + i=1; + } + + } + + t = libnet_build_tcp (tx.sp, + tx.dp, + tx.tcp_seq, + tx.tcp_ack, + tx.tcp_control, + tx.tcp_win, + tx.tcp_sum, + tx.tcp_urg, + tx.tcp_len, + (tx.tcp_payload_s) ? tx.tcp_payload : NULL, + tx.tcp_payload_s, + l, + t); + + if (t == -1) + { + fprintf(stderr, " mz/update_TCP_SQNR: Can't build TCP header: %s\n", libnet_geterror(l)); + exit (0); + } + + return i; +} + + +//////////////////////////////////////////////////////////////////////// +// +// + +int print_frame_details() +{ + unsigned char *dum1, *dum2; + char pld[65535]; + char sa[32], da[32]; + + if (!tx.packet_mode) + { + bs2str(tx.eth_dst, da, 6); + bs2str(tx.eth_src, sa, 6); + fprintf(stderr, " Eth: DA = %s, SA = %s\n",da,sa); + } + + + if (tx.dot1Q) + { + fprintf(stderr, " 802.1Q VLAN-TAG = %s\n", tx.dot1Q_txt); + } + + if (tx.mpls) + { + fprintf(stderr," MPLS labels (label:exp:bos:ttl): %s\n",tx.mpls_verbose_string); + + } + + + dum1 = (unsigned char*) &tx.ip_src_h; + dum2 = (unsigned char*) &tx.ip_dst_h; + (mode==IP) ? (void) bs2str(tx.ip_payload, pld, tx.ip_payload_s) : strcpy(pld, "[see next layer]"); + + if (ipv6_mode) { + char src6[64]; char dst6[64]; + libnet_addr2name6_r(tx.ip6_src, LIBNET_DONT_RESOLVE, src6, 64); + libnet_addr2name6_r(tx.ip6_dst, LIBNET_DONT_RESOLVE, dst6, 64); + + fprintf(stderr," IP: ver=6, dscp=%u, flow=%u, len=%u, next=%u, hop=%u " + "SA=%s, DA=%s\n payload=%s\n", tx.ip_tos, tx.ip_flow, + tx.ip_len, tx.ip_proto, tx.ip_ttl, src6, dst6, pld); + } + else { + fprintf(stderr," IP: ver=4, len=%u, tos=%u, id=%u, frag=%u, ttl=%u, proto=%u, sum=%u, " + "SA=%u.%u.%u.%u, DA=%u.%u.%u.%u,\n" + " payload=%s\n", tx.ip_len, tx.ip_tos, + tx.ip_id, tx.ip_frag, tx.ip_ttl, tx.ip_proto, tx.ip_sum, + *(dum1+3),*(dum1+2),*(dum1+1),*(dum1), *(dum2+3),*(dum2+2),*(dum2+1),*(dum2+0), pld); + } + + if ((mode==UDP)||(mode==DNS)||(mode==RTP)) + { + bs2str(tx.udp_payload, pld, tx.udp_payload_s); + fprintf(stderr, " UDP: sp=%u, dp=%u, len=%u, sum=%u, \n" + " payload=%s\n", tx.sp, tx.dp, tx.udp_len, tx.udp_sum, pld); + } + if (mode==TCP) // TODO: Improve message details (flags, ...) + { + bs2str(tx.tcp_payload, pld, tx.tcp_payload_s); + fprintf(stderr, " TCP: sp=%u, dp=%u, S=%u, A=%u, flags=%x, win=%u, len=%u, sum=%u, \n" + " payload=%s\n", + tx.sp, tx.dp, tx.tcp_seq, tx.tcp_ack, tx.tcp_control, tx.tcp_win, tx.tcp_len, tx.tcp_sum, pld); + } + + // send_icmp must prepare the verbose string because there are many + // different types of ICMP packets... + if (mode==ICMP) + { + fprintf(stderr, " %s\n", tx.icmp_verbose_txt); + } + + if (mode==ICMP6) + { + fprintf(stderr, " %s\n", tx.icmp_verbose_txt); + } + + // libnet_diag_dump_pblock(l); + fprintf(stderr,"\n"); + + if (simulate) + { + fprintf(stderr, "*** NOTE: Simulation only! Nothing has been sent! ***\n"); + exit(0); + } + + + return 0; +} + diff --git a/staging/mops.c b/staging/mops.c new file mode 100644 index 0000000..f56deff --- /dev/null +++ b/staging/mops.c @@ -0,0 +1,769 @@ +/* + * 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" + + + + +// Creates first element, aka "head" element +// This element can also be used! See mops_alloc_packet! +// +struct mops * mops_init() +{ + // 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; j<MAX_MOPS_COUNTERS_PER_PACKET; j++) { + new_mops->counter[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; i<fs; i++) + { + if ((i>0) && (!(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; i<device_list_entries; i++) { + if (strncmp(device_list[i].dev, devname, 16)==0) { + return i; + } + } + + return -1; +} + + + +// Assign device-specific values (source IP and MAC addresses), +// drawn from global device table, to the specified MOPS entry +// with index i. +// +int mops_use_device(struct mops * mp, int i) +{ + // Assign source MAC address + // Assign source IP address + // TODO? Assign default gateway + + memcpy((void *) &mp->eth_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; +} + + diff --git a/staging/mops.h b/staging/mops.h new file mode 100644 index 0000000..fd9884c --- /dev/null +++ b/staging/mops.h @@ -0,0 +1,1023 @@ +/* + * 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 + * +*/ + + +#ifndef __MOPS__ +#define __MOPS__ + + +#define MOPS_VERSION "0.3" +#define MOPS_CODENAME "Cyanistes caeruleus (DE+150)" +#define AUTOMOPS_ENABLED 0 // Automops subsystem (currently in development) +#define MAX_MOPS_FRAME_SIZE 8192 // total max frame size (=all headers plus payload) +#define MIN_MOPS_FRAME_SIZE 15 // total min frame size +#define MOPS_SIZE_MARGIN 50 // User limit: MAX_MOPS_FRAME_SIZE - MOPS_SIZE_MARGIN +#define MAX_MOPS_MSG_SIZE 7500 // payload limit +#define MAX_MOPS_MSG_CHUNK_SIZE 1000 // Chunks size when read data from a file for the payload +#define MAX_MOPS_COUNTERS_PER_PACKET 10 // number of user-defined counters per packet +#define MAX_MOPS_PACKET_NAME_LEN 32 // Each packet must have an unique name +#define MAX_MOPS_DESCRIPTION_LEN 80 // Max length of packet description string +#define MAX_MOPS_DOT1Q_TAGS 64 // Max number of 802.1Q tags within a frame (too many, practically ;-)) +#define MAX_MOPS_MPLS_TAGS 64 // Max number of MPLS tags within a frame (again too many, practically) +#define XN_MAX_STACK 7 // max nesting depth + +#define AUTOMOPS_MAX_FILE_SIZE 200000 // Max file size in bytes for AMP protocol definitions +#define AUTOMOPS_MAX_NAME_LEN 32 // used for all names (valname, field name, protocol name) +#define AUTOMOPS_MAX_SHORTDESC_LEN 64 + +#define XML_MAX_TAG_LEN 16 +#define XML_STRLEN 64 // required length of user string to hold tag + // but also alternatively an error message + + +#define MAX_LLDP_OPT_TLVS 500 // How many bytes are reserved for optional TLVs within an LLDP message? + +//#define MAX_MOPS_PACKETS 1000 // number of packet slots *** DEPRECATED *** +#define MAX_CLI_LINE_BYTES 32 // How many bytes 'mops_print_frame' should print before line break + +// Basic layers; see mops_clear_layers() +// Also used by automops (see layers_on, layers_off) +#define MOPS_ALL 127 +#define MOPS_ETH 1 +#define MOPS_SNAP 2 // either LLC, LLC+SNAP +#define MOPS_dot1Q 4 +#define MOPS_MPLS 8 +#define MOPS_IP 16 +#define MOPS_UDP 32 +#define MOPS_TCP 64 + +// The following definitions are needed as values for (int) p_desc_type +// which identifies the exact type of (void *) p_desc. +#define MOPS_NO_PDESC 100 +#define MOPS_ARP 101 +#define MOPS_BPDU 102 +#define MOPS_CDP 103 +#define MOPS_DNS 104 +#define MOPS_ICMP 105 +#define MOPS_LLDP 106 +#define MOPS_RTP 107 +#define MOPS_SYSLOG 108 +#define MOPS_IGMP 109 + +// packet states (variable 'state') +// NOTE: every state >2 (i. e. 3, 4, ...) is an active state, i. e. packet should +// be blocked from configurations etc. +#define MOPS_STATE_NULL 0 // transition state, only initially +#define MOPS_STATE_INIT 1 +#define MOPS_STATE_CONFIG 2 // normal state (when configured) +#define MOPS_STATE_ACTIVE 3 // has associated sending thread +#define MOPS_STATE_SEQACT 4 // packet is member of an active sequence + +// Return values of mops_pdesc utility functions (see mops_ext.c) +#define MOPS_PDESC_SUCCESS 0 // Value assigned properly | string present +#define MOPS_PDESC_FAILURE 1 // Unspecified problem | string not present +#define MOPS_PDESC_LOW 2 // Value smaller than lower bound - but will set +#define MOPS_PDESC_HIGH 3 // Value larger than upper bound - but will set +#define MOPS_PDESC_OVERFLOW 4 // Value exceeded possible range +#define MOPS_PDESC_NO_MAC 5 // Invalid MAC address +#define MOPS_PDESC_NO_IP 6 // Invalid IP address + +// These definitions are (should be) only used in mops_ext.c +#define MOPS_EXT_ARP struct mops_ext_arp * +#define MOPS_EXT_BPDU struct mops_ext_bpdu * +#define MOPS_EXT_CDP struct mops_ext_cdp * +#define MOPS_EXT_DNS struct mops_ext_dns * +#define MOPS_EXT_ICMP struct mops_ext_icmp * +#define MOPS_EXT_LLDP struct mops_ext_lldp * +#define MOPS_EXT_RTP struct mops_ext_rtp * +#define MOPS_EXT_SYSLOG struct mops_ext_syslog * +#define MOPS_EXT_IGMP struct mops_ext_igmp * + +// Very specific definitions here: +#define MOPS_RTP_EXT_MZID 0xcaca // first 16 bit of the Mausezahn RTP extension header +#define DSP_SOURCE 100 // any number >0 indicating /dev/dsp to be used as RTP payload +#define MOPS_RTP_MAX_PAYLOAD_SIZE 200 + +#include <pthread.h> + + +// These are initialized with the definitions MIN_MOPS_FRAME_SIZE and +// MAX_MOPS_FRAME_SIZE above but can be overridden by the user (without +// extending these limits) +unsigned int min_frame_s; +unsigned int max_frame_s; + +struct mops_counter +{ + int use; // 1 = counter active + int offset; // points to counter location in *msg* + int random; // 1=random, 0=use start/stop/step + u_int32_t start; // HOST BYTE ORDER + u_int32_t stop; // HOST BYTE ORDER + u_int32_t step; // HOST BYTE ORDER + u_int32_t cur; // current value (HOST BYTE ORDER) + int bytes; // number of bytes used (1|2|4) - selects hton2 or hton4 + // and enables proper wraparounds (mod 256, mod 65536, ...) +}; + + +enum amperr { + ampSuccess, + ampInvalidIndex, + ampInvalidName, + ampDuplicateName, + ampDescTooLong, + ampInvalidType, + ampInvalidLayer, + ampTCPandUDP, + ampUnknownKeyword, + ampSingleWordRequired, + ampRangeError, + ampPayloadLen, + ampPayloadType, + ampUnknownTag +}; + +enum fieldtypes { + Byte8, Byte16, Byte32, Flag_in_Byte, MultiBytes, MultiBytesHex, + TLV // TODO: different/standard TLV formats (Cisco CDP, LLCP, ...) +}; + + +struct fields { + struct fields *next; + char name[AUTOMOPS_MAX_NAME_LEN+1]; // Official name of field -- CASE INSENSITIVE + char shortdesc[AUTOMOPS_MAX_SHORTDESC_LEN+1]; // One-line description + char * longdesc; // Long (multiline) description (helptext) + enum fieldtypes type; // Field type corresponds to length + int constant; // 1: only default value allowed, not changeable + + int i; // unique internal field entry index (strongly monotonic increasing!) + // Note: first entry starts with 0. + + int index; // protocol field index; Note: First field has index 1. + // successive fields have same index in two cases: + // 1) several flags within same byte + // 2) several different valname/val pairs for same field index. In this + // case the successive field-entries must only contain the valname + // and a corresponding value. + + // may contain a reserved value *name*, usually used with multiple + // successive fields with same field index N. + char valname[AUTOMOPS_MAX_NAME_LEN+1]; + + u_int32_t + tlv_type, + tlv_len, + val, // default initial value + min, // range min value + max; // range max value + + int leftshift; // when type=Flag_in_Byte + + u_int8_t *str; // default initial characters or hex values (when type=MultiByte or TLV) + int str_s; // length of str +}; + + +// Each automops object identifies another dynamically specified protocol. +// +// Usage and structure: +// +// 1) Doubly linked list to store new (dynamically defined) protocols. +// Protocol definitions are typically loaded from a file and converted +// to an automops entry via parse_protocol() defined in parse_xml.c +// +// 2) When the user chooses one of these protocols to be used for a mops +// then best is to copy the whole automops to the current mops; this +// way the protocol's field values can be easily modified and +// automops_update() can be directly applied to that automops entity. +// +// If you cannot understand anything you are maybe already mausezahn'ed ;-) +// +struct automops { + struct automops *next; + struct automops *prev; + + char name[AUTOMOPS_MAX_NAME_LEN+1]; // Protocol name + char desc[AUTOMOPS_MAX_SHORTDESC_LEN+1]; // One-line description + + // Specify required and allowed layers using the definitions above + // for example MOPS_ETH, MOPS_SNAP, MOPS_dot1Q, MOPS_MPLS, + // MOPS_IP, MOPS_UDP, and MOPS_TCP + int + layers_on, // which layers are REQUIRED + layers_off; // which layers MUST be DISABLED because of conflicts + // Not mentioned layers are arbitrary (e. g. MOPS_dot1Q) + // Protocol-specific addresses + // Usually only destination address/port is specific but there are some + // exceptions (e. g. DHCP uses well known sp/dp pair). + // Value zero means ignore; otherwise copy to mops. + u_int16_t etype; // EtherType + u_int8_t proto; // IP protocol number + u_int8_t sa[6], da[6]; // source/destination MAC address + u_int32_t SA, DA; // source/destination IPv4 address + int sp, dp; // Well-known port numbers + + + int payload_type; // 0=none, 1=ascii, 2=hex, 3=any + char *payload; // default payload data (if above is true) + int payload_s; + + struct fields *field; // points to single linked list describing each field + // or NULL + + /// ---- internal data ----- + int defined_externally; // 0=built-in, 1=file, -1=undefined + int used; // number of mopses using this automops; + // = -1 when allocated + // = 0 when got valid data + // = >0 when used by some mopses +}; + + +struct automops * amp_head; + + +struct mops +{ + struct mops *next; + struct mops *prev; + + // *** The Header *** + // Management issues for TX + int state; // see above + int id; // UNIQUE Identifier (NOTE: MUST ALLOW -1) + int mz_system; // identifies user and system packets (such as ARP) + int verbose; // Be more or less verbose when processing that MOPS + char packet_name[MAX_MOPS_PACKET_NAME_LEN]; // Each packet must have unique name + char description[MAX_MOPS_DESCRIPTION_LEN]; // An optional short packet description + + pthread_t mops_thread; // associated transmission thread + pthread_t interval_thread; + + pthread_mutex_t mops_mutex; // mutex to savely access mops data + + char device[16]; // every packet could be sent through a different device + // NOTE that we do NOT store the index of device_list[] because after + // a re-discovery of the network interfaces the same index could map + // to a different physical network device. Instead the device's name + // does not change (however, might be not available, but then we report + // an error message and the user can assign another interface) + // + // See function mops_get_device_index() + + unsigned long count; // Desired number of packets to be sent. 0 means infinite. + unsigned long cntx; // This value actually counts sent packets. + // NOTE: Count _down_ for finite count, count _up_ for infinite count. + + struct timespec ndelay; // Inter-packet delay; contains two members: + // tv_sec and tv_nsec (0 to 999999999) + + struct timespec interval; // An optional global interval + int interval_used; // 0=none, 1=configured, 2=active (i. e. interval_thread is valid) + + struct timespec delay_sigma; // Standard deviation + + int delay_pd; // Which propability distribution (density) + // MOPS_DELAY_GAUSS + // MOPS_DELAY_EXP will result in a Poisson process with lambda=delay + + + int auto_delivery_off; // 0 means, the destination MAC address will be chosen automatically (for IP packets) + // depending on the IP destination address ('direct or indirect delivery', i. e. based + // on ARP). + // + // 1 means, the user-provided destination MAC address will be used. + + // ****************** + + // Data section + + int + use_ETHER, // if unset (=0) then complete raw frame given in frame[] + use_SNAP, // NOTE: use_SNAP=1 may indicate either 802.3+LLC alone or 802.3+LLC+SNAP + use_dot1Q, + use_MPLS, + use_IP, + use_UDP, + use_TCP; + + int // pointers to important positions + begin_IP, // marks byte position of IP header within frame + begin_UDP, // marks byte position of UDP header within frame + begin_TCP, // marks byte position of TCP header within frame + begin_MSG; // marks byte position of first message byte (=payload) within frame + + int // **** get payload (message) from a file **** + MSG_use_RAW_FILE, // 1 means update function should copy next chunk from file + MSG_use_HEX_FILE, // same but assumes file content such as "aa:bb:cc:f3:1e:..." + MSG_use_ASC_FILE; // same but interpretes file content as ASCII characters + // NOTE: if one of these are set to 1 then a filepointer is open !!! + + // A protocol descriptor (p_desc) is only used for some statically + // defined protocols. Originally intended for more complicated protocols + // such as DNS. + void * p_desc; // optionally points to protocol descriptor (e. g. for DNS, CDP, etc) + int p_desc_type; // identifies the exact type of p_desc + + + + // AutoMOPS provides a dynamic method to define new protocols. Here we need a pointer + // to the protocol definition for convenience and the complete protocol header field + // which is created by automops_update() + // + // Note: The used 'amp' should be memcpy'd for this particular mops + // because then we can store current PDU values here and the + // user can modify it later arbitrarily. + // + // Use automops_clone_automops() in automops.c for this. + // + struct automops *amp; // points to protocol definition + u_int8_t *amp_pdu; // contains the complete PDU as bytestring + int amp_pdu_s; + + + // Resulting frame: + u_int8_t frame[MAX_MOPS_FRAME_SIZE]; // will hold the complete frame + u_int32_t frame_s; // indicates the total frame size + + + // Ethernet parameters: + u_int8_t eth_dst[6]; + u_int8_t eth_src[6]; + int eth_src_israndom; // if set to 1 then the source address is to be randomized + u_int16_t eth_type; + u_int16_t eth_type_backup; // if original type must be restored (e. g. when removing MPLS labels) + + // 802.3 parameters: LLC/SNAP + u_int16_t eth_len; + u_int8_t eth_snap[16]; // AA-AA-03-<OUI>-<TYPE> + int eth_snap_s; // usually 8 bytes + + + // 802.1Q VLAN Tag !!! NOTE: outer tag has lower index number (same byte-order as in frame[]) !!! + u_int8_t dot1Q[MAX_MOPS_DOT1Q_TAGS*4]; // All successive 802.1Q/P headers, 4 bytes per header: 0x8100, pri, cfi, id + int dot1Q_s; // how many bytes from above are really used + int dot1Q_isrange; // if 1, only the outer tag loops through the range. + int dot1Q_start; + int dot1Q_stop; + + + // MPLS label stack + u_int8_t mpls[MAX_MOPS_MPLS_TAGS*4]; // All successive labels + int mpls_s; // how many bytes from above are really used + int mpls_isrange; // if 1, only the outer tag loops through the range. + int mpls_start; + int mpls_stop; + + // IP parameters -- NOTE: Everything here is in HOST BYTE ORDER !!! + + u_int32_t ip_src; // By default interface address + u_int32_t ip_src_start; // start of range (HOST byte order => easy to count) + u_int32_t ip_src_stop; // stop of range (HOST byte order => easy to count) + int ip_src_isrange; // if set to 1 then the start/stop values above are valid. + int ip_src_israndom; // if set to 1 then the source address is to be randomized + u_int32_t ip_dst; // (HOST byte order) + u_int32_t ip_dst_start; // start of range (NOT network byte order => easy to count) + u_int32_t ip_dst_stop; // stop of range (NOT network byte order => easy to count) + int ip_dst_isrange; // if set to 1 then the start/stop values above are valid. + u_int16_t + ip_len, + ip_id, + ip_frag_offset, // 13 bit Offset: allowed values: 0..8191 + ip_sum; // TODO: provide variable 'ip_sum_false' to create false checksum for various tests + int ip_IHL_false; // Default=0, set to 1 if user configured own (typically false) header length + int ip_len_false; // Default=0, set to 1 if user configured own (typically false) total length + int ip_sum_false; // Default=0, set to 1 if user configured own (typcially false) checksum + u_int8_t + ip_version, + ip_IHL, // header length (4 bits = 0..15) + ip_tos, + ip_flags_RS, // 0|1 ... Reserved flag "must be zero" + ip_flags_DF, // 0|1 ... Don't Fragment + ip_flags_MF, // 0|1 ... More Fragments + ip_fragsize, // if >0 it activates auto-fragmentation + ip_frag_overlap, // if >0 then all fragments overlap. Must be multiple of 8 but smaller than fragsize. + ip_ttl, + ip_proto; + u_int8_t + ip_option[1024]; // Any IP Option used? + int ip_option_used; // >0 if yes. The exact number also indicates which option(s) used - see mops_ip.c + u_int32_t + ip_option_s; + + + // General L4 parameters: + u_int16_t + sp, dp, + sp_start, sp_stop, + dp_start, dp_stop; + int + sp_isrand, // if set to 1 then use random port number for each sent packet + dp_isrand, // if set to 1 then use random port number for each sent packet + sp_isrange, // if set to 1 then start/stop values above are valid + dp_isrange; // if set to 1 then start/stop values above are valid + + // UDP parameters + u_int16_t + udp_len, // includes header size (8 bytes) + udp_sum; + int udp_sum_false; // Default=0, set to 1 if user configured own (typcially false) checksum + int udp_len_false; // Default=0, set to 1 if user configured own (typcially false) length + + // TCP parameters (RFC 793) + // + // 0 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | Source Port | Destination Port | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | Sequence Number | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | Acknowledgment Number | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | Data | |U|A|P|R|S|F| | + // | Offset| Reserved |R|C|S|S|Y|I| Window | + // | | |G|K|H|T|N|N| | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | Checksum | Urgent Pointer | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | Options | Padding | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | data | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + u_int32_t + tcp_seq, + tcp_seq_start, + tcp_seq_stop, + tcp_seq_delta, // Also used instead of an 'isrange' variable + tcp_ack, + tcp_ack_start, + tcp_ack_stop, + tcp_ack_delta; // Also used instead of an 'isrange' variable + u_int8_t + tcp_offset, // Header length in multiples of 32 bit (4 bit value, 0..15) + tcp_res, // reserved (4 bits) + tcp_ctrl_CWR, // 0|1 - Congestion Window Reduced [RFC-3168] + tcp_ctrl_ECE, // 0|1 - ECN-Echo [RFC-3168] + tcp_ctrl_URG, // 0|1 + tcp_ctrl_ACK, // 0|1 + tcp_ctrl_PSH, // 0|1 + tcp_ctrl_RST, // 0|1 + tcp_ctrl_SYN, // 0|1 + tcp_ctrl_FIN; // 0|1 + u_int16_t + tcp_win, + tcp_sum, + tcp_urg, + tcp_len; // Only needed for the checksum calculation and is not transmitted (host order!) + + int + tcp_sum_false, // Default=0, set to 1 if user configured own (typcially false) checksum + tcp_offset_false; // Default=0, set to 1 if user configured own (typcially false) offset + u_int8_t + tcp_option[1024]; + u_int32_t + tcp_option_s; + int tcp_option_used; // >0 if yes. The exact number also indicates which option(s) used - see mops_tcp.c + + + // Message: + u_int8_t msg[MAX_MOPS_MSG_SIZE]; + u_int32_t msg_s; + FILE *fp; // points to file if MSG_use_RAW_FILE or MSG_use_HEX_FILE or MSG_use_ASC_FILE is set to 1 + u_int32_t chunk_s; // max chunk size to be copied from file + + + // User-defined counters: + struct mops_counter counter[MAX_MOPS_COUNTERS_PER_PACKET]; + int used_counters; // number of currently defined counters + +}; + + + +struct mops_ext_arp +{ + u_int16_t hw_type; + u_int16_t pr_type; + u_int8_t hw_size; + u_int8_t pr_size; + u_int16_t opcode; + u_int8_t sender_mac[6]; + u_int8_t sender_ip[4]; + u_int8_t target_mac[6]; + u_int8_t target_ip[4]; + u_int16_t trailer; +}; + + + +struct mops_ext_bpdu // TODO +{ + u_int16_t id; + u_int8_t version; // 0=802.1D, 2=RSTP(802.1w) + u_int8_t bpdu_type; // 0=conf, 1=topology change (actually in big endian!), 2=RSTP/MSTP + u_int8_t flags; // X... .... = TCN ACK + // .X.. .... = Agreement + // ..X. .... = Forwarding + // ...X .... = Learning + // .... XX.. = Port Role (e. g. 11=Desgn) + // .... ..X. = Proposal + // .... ...X = TCN + u_int8_t root_id[8]; // Root BID + u_int32_t root_pc; // Root Path Cost + u_int8_t bridge_id[8]; // Own BID + u_int16_t port_id; // Port Identifier + u_int16_t message_age; // All timers are multiples of 1/256 sec. Thus times range from 0 to 256 seconds. + u_int16_t max_age; + u_int16_t hello_time; + u_int16_t f_delay; + u_int8_t trailer[8]; // either all-zero or 34:00:02:VLAN(16bit):00:00 when PVST+ + + int rstp; // 1 = RSTP + int pvst; // 1=PVST+ , 0 = 802.1D + int mstp; // 1 = Multiple Instance STP + +}; + +struct mops_ext_lldp { + int non_conform; // if 1 then the order of TLVs is arbitrary + int chassis_id_subtype; + int chassis_id_len; + u_int8_t *chassis_id; + int port_id_subtype; + int port_id_len; + u_int8_t *port_id; + int TTL; + int optional_tlvs_s; + u_int8_t *optional_tlvs; + +}; + +enum igmp_type {IGMP_GENERAL_QUERY, + IGMP_GSPEC_QUERY, + IGMP_V2_REPORT, + IGMP_V1_REPORT, + IGMP_LEAVE}; + +struct igmp_sa_struct { // For single linked list to hold unicast addresses for IGMPv3 query + u_int32_t sa; + struct igmp_sa_struct *next; +}; + +struct igmp_aux_struct { // For single linked list to hold auxilary data for IGMPv3 report + u_int32_t aux_data; + struct igmp_aux_struct *next; +}; + + +struct igmp_group_struct { // For single linked list to hold IGMPv3 group records + u_int8_t record_type; + u_int8_t aux_data_len; + u_int16_t nr_sources; + u_int32_t mcast_addr; + struct igmp_sa_struct *sa_list; + struct igmp_aux_struct *aux_list; + struct igmp_group_struct *next; +}; + + + +struct mops_ext_igmp { + int version; // internal, not in header + u_int8_t type; + u_int8_t max_resp_code; // equally: 'max response time' for IGMPv2 + u_int16_t sum; + int sum_false; // if '1' then sum contains user-provided checksum; if '0' then autocompute! + u_int32_t group_addr; + u_int8_t + resv4, // resv4 + S + QRV => one byte in IGMPv3 query + S, // S = Suppress Router-Side Processing + QRV; // QRV = Querier's Robustness Variable + u_int8_t resv8; // needed in IGMPv3 response AND IGMPv1 query+response + u_int16_t resv16; // needed in IGMPv3 response + u_int8_t QQIC; // Querier's Query Interval Code + u_int16_t nr_entries; // either number of sources (=query) or group records (=response) + struct igmp_sa_struct *sa_list; +}; + + +struct mops_ext_cdp // TODO +{ + u_int8_t id; + u_int16_t hw_type; +}; + +struct mops_ext_dns // TODO: complete +{ + // Main 16-bit fields + u_int16_t id; + u_int16_t num_queries; + u_int16_t num_answers; + u_int16_t num_author; + u_int16_t num_add; + u_int16_t type; + + // Flags (1 bit, except where noted) + u_int8_t qr; + u_int8_t opcode; // 4 bits + u_int8_t aa; + u_int8_t tc; + u_int8_t rd; + u_int8_t ra; + u_int8_t z; // 3 bits + u_int8_t rcode; // 4 bits + +}; + + +struct mops_ext_icmp // TODO +{ + u_int8_t id; + u_int16_t hw_type; +}; + +struct mops_ext_rtp +{ + // Vars to hold flag values: + u_int8_t v, + p, + x, // only sets the flag; if you really want an extension header also set "x_type" (see below) + cc, // csrc_count visible in header (has no further meaning, thus support for "wrong" headers) + cc_real, // real csrc_count (only used internally to create CSRC list) + m, + pt; // selects inter-packet delay and payload_s; + + u_int16_t sqnr; // initial sqnr + u_int32_t tst; // initial timestamp + u_int32_t ssrc; // !!! also used to identify measurement streams !!! + u_int32_t csrc[16]; // NOTE: only up to 15 CSRC's are allowed according RFC 3550 + + // additionally: + int tst_inc; // The increment of the tst (depends on codec) + u_int8_t payload[MOPS_RTP_MAX_PAYLOAD_SIZE]; // + int payload_s; // is the same as tst_inc when codec is G.711 but different with other codecs! + int source; // Optionally draw data from file or /dev/dsp or such [TODO] + int rtp_header_len; // will be set by mops_update_rtp() + // one optional header extension: + int x_type; // IMPORTANT: which extension header to use: 0 = none, 42 = Mausezahn, 1 = Aero + u_int8_t extension[64]; // a user configurable extension header [CURRENTLY UNUSED] +}; + + + +struct mops_ext_syslog //TODO +{ + u_int16_t hw_type; + u_int16_t pr_type; +}; + + +///////////////////////////////////////////////////////////////// + +struct mops *mp_head; // This global will point to the head of the mops list + +///////////////////////////////////////////////////////////////// +// MOPS Prototypes: + +void mops_hton2 (u_int16_t *host16, u_int8_t *net16); +void mops_hton4 (u_int32_t *host32, u_int8_t *net32); + +int mops_get_proto_info (struct mops *mp, char *layers, char *proto); + +// Inserts value in 'flag' (up to 7 bits are useful) into the target +// with an optional left-shift. For example if flag contains a 4-bit value +// and should be placed within the target in bit positions 3-6 like: +// +// 7 6 5 4 3 2 1 0 +// +--+--+--+--+--+--+--+--+ +// | | FLAGS | | | | +// +--+--+--+--+--+--+--+--+ +// +// then simply call: +// +// (void) mops_flags ( &target, &flag, 3 ); +// +// Note: +// 1) shift=0 means no shift +// 2) Because of speed we do not check if the arguments are reasonable +// +void mops_flags (u_int8_t *target, u_int8_t *flag, int shift); + +u_int16_t mops_sum16 (u_int16_t len, u_int8_t buff[]); + +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, char* str); +struct mops * mops_search_name (struct mops* list, char *key); +struct mops * mops_search_id (struct mops* list, u_int32_t key); + +void mops_delete_all (struct mops* list); +void mops_cleanup (struct mops* list); + +// State functions +int mops_state (struct mops *mp); +int mops_is_active (struct mops *mp); +void mops_set_conf (struct mops *mp); +void mops_set_active (struct mops *mp); +void mops_set_seqact (struct mops *mp); +int mops_is_seqact (struct mops *mp); +int mops_is_any_active (struct mops *mp); + +// For debugging purposes +int mops_print_frame (struct mops *mp, char *str); + +// sets UDP or TCP checksum within mp->frame +// TODO: copying the whole segment is ugly and slow; +// make it more efficient and realize it in-place. +// +int mops_get_transport_sum (struct mops *mp); + +// returns new counter index for given packet +// or -1 if all counters used already +int mops_get_counter (struct mops *mp); + +// This is the very basic MOPS update function. It simply updates the whole +// MOPS frame specified by pointer mp. If you only want to update specific +// details then please see the other related specialized functions which are +// faster. +int mops_update (struct mops *mp); + +int mops_set_defaults (struct mops *mp); + +// Get global device index for a given device name. +int mops_get_device_index(char *devname); + +// Assign device-specific addresses to packet. +int mops_use_device(struct mops * clipkt, int i); + +// Find and returns a new unique packet id +// If none can be found, returns -1. +int mops_get_new_pkt_id (struct mops *mp); + +// Simply sets specified 'layer switches' in struct mops to zero +int mops_clear_layers (struct mops *mp, int l); + +// Transmission functions +int mops_tx_simple (struct mops *mp); +void *mops_tx_thread_native (void *arg); +void *mops_interval_thread (void *arg); +void *mops_sequence_thread (void *arg); + + +int mops_destroy_thread (struct mops *mp); + +// Utility functions for packet headers (aka *** METHODS *** for the object-oriented nerds) +int mops_dot1Q_remove (struct mops *mp, int k); +int mops_dot1Q_nocfi (struct mops *mp, int k); +int mops_dot1Q_cfi (struct mops *mp, int k); +int mops_dot1Q (struct mops *mp, int i, int m, u_int16_t v, u_int16_t c); + +int mops_mpls_remove (struct mops *mp, int j); +int mops_mpls_bos (struct mops *mp, int k); +int mops_mpls_nobos (struct mops *mp, int k); +int mops_mpls(struct mops *mp, int i, int m, u_int32_t Label, u_int8_t Exp, u_int8_t TTL); + +int mops_ip_get_dst_mac(struct device_struct *dev, u_int8_t *ip, u_int8_t *mac); +int mops_ip_dscp(struct mops *mp, char *argv); +int mops_ip_tos (struct mops* mp, int ipp, int tos, int mbz); +int mops_ip_option_ra (struct mops* mp, int value); +int mops_ip_option_remove_all (struct mops* mp); + +u_int32_t mops_tcp_complexity_sqnr (struct mops * mp); +u_int32_t mops_tcp_complexity_acknr (struct mops * mp); + +// Prints current flag settings in the provided string 'str'. +int mops_tcp_flags2str (struct mops* mp, char *str); + +int mops_tcp_add_option (struct mops* mp, + int mss, + int sack, + int scale, + u_int32_t tsval, + u_int32_t tsecr); + + +////////////////////////////////////////////////////////////////////////////// +// +// ****** The following are important to easily create new packet types ****** +// +////////////////////////////////////////////////////////////////////////////// + +// Adds single byte to msg +int mops_msg_add_byte (struct mops *mp, u_int8_t data); + +// Adds bit field in *previous* msg-byte using optional left-shift +int mops_msg_add_field (struct mops *mp, u_int8_t data, int shift); + +// Adds two bytes in network byte order to msg +int mops_msg_add_2bytes (struct mops *mp, u_int16_t data); + +// Adds four bytes in network byte order to msg +int mops_msg_add_4bytes (struct mops *mp, u_int32_t data); + +// Adds string of bytes with lenght len +int mops_msg_add_string (struct mops *mp, u_int8_t *str, int len); + +// Add counter to message +int mops_msg_add_counter (struct mops *mp, + int random, // 1=random, 0=use start/stop/step + u_int32_t start, // HOST BYTE ORDER + u_int32_t stop, // HOST BYTE ORDER + u_int32_t step, // HOST BYTE ORDER + int bytes // number of bytes used (1|2|4) - selects hton2 or hton4 + ); + +// Returns 0 if identical, 1 if different +int compare_ip (u_int8_t *ip1, u_int8_t *ip2); + +// Returns 0 if identical, 1 if different +int compare_mac (u_int8_t *mac1, u_int8_t *mac2); + +// Converts a 'struct timespec' value into a human readable string +int timespec2str(struct timespec *t, char *str); + +// ------------------------------------------------------------------------------- + +// Add protocol descriptor of type ptype +// +// Smart behaviour: If a p_desc has been already assigned, this function +// clears and frees everything before assigning another p_desc structure. +// +int mops_ext_add_pdesc (struct mops *mp, int ptype); + +// Create msg based on p_desc data. +// After that call mops_update and the frame is complete. +int mops_ext_update (struct mops *mp); + +// Delete any protocol descriptor +int mops_ext_del_pdesc (struct mops *mp); + +// Initialization functions for p_desc +int mops_init_pdesc_arp(struct mops *mp); +int mops_init_pdesc_bpdu(struct mops *mp); +int mops_init_pdesc_cdp(struct mops *mp); +int mops_init_pdesc_dns(struct mops *mp); +int mops_init_pdesc_icmp(struct mops *mp); +int mops_init_pdesc_igmp(struct mops *mp); +int mops_init_pdesc_lldp(struct mops *mp); +int mops_init_pdesc_syslog(struct mops *mp); +int mops_init_pdesc_rtp(struct mops *mp); + +int mops_create_igmpv2 (struct mops *mp, + int override, // normally zero, but if '1' the user want to override defaults + int igmp_type, // IGMP_GENERAL_QUERY, IGMP_GSPEC_QUERY, IGMP_V2_REPORT, IGMP_V1_REPORT, IGMP_LEAVE + int mrt, // max response time + int sum, //-1 means auto-compute, other values means 'use this user-defined value' + u_int32_t group_addr); + + +// Update functions for p_desc => msg +int mops_update_arp(struct mops * mp); +int mops_update_bpdu(struct mops * mp); +int mops_update_igmp (struct mops * mp); +int mops_update_lldp (struct mops * mp); +int mops_update_rtp (struct mops * mp); +int mops_update_rtp_dynamics (struct mops * mp); + +// Utility functions for p_desc +int mops_pdesc_mstrings (char *dst, char* argv[], int argc, int max); +int mops_pdesc_1byte (u_int8_t *dst, char* usr, int spec, int min, int max); +int mops_pdesc_2byte (u_int16_t *dst, char* usr, int spec, int min, int max); +int mops_pdesc_4byte (u_int32_t *dst, char* usr, int spec, unsigned long int min, unsigned long int max); +int mops_pdesc_mac (u_int8_t *dst, char* usr); +int mops_pdesc_ip (u_int8_t *dst, char* usr); + +// Other p_desc related functions +int mops_create_bpdu_bid(struct mops * mp, int pri, int esi, char *mac, int bid_or_rid); +int mops_create_bpdu_trailer (struct mops * mp, u_int16_t vlan); +int mops_lldp_tlv (u_int8_t *tlv, int type, int len, u_int8_t *value); +int mops_lldp_tlv_chassis (u_int8_t *tlv, int subtype, int len, u_int8_t *cid); +int mops_lldp_tlv_port (u_int8_t *tlv, int subtype, int len, u_int8_t *pid); +int mops_lldp_tlv_TTL (u_int8_t *tlv, int ttl); +int mops_lldp_tlv_end (u_int8_t *tlv); +int mops_lldp_opt_tlv_bad (struct mops *mp, int type, int badlen, int len, u_int8_t *value); +int mops_lldp_opt_tlv_org (struct mops *mp, int oui, int subtype, int len, u_int8_t *inf); +int mops_lldp_opt_tlv_chassis (struct mops *mp, int subtype, int len, u_int8_t *cid); +int mops_lldp_opt_tlv_port (struct mops *mp, int subtype, int len, u_int8_t *pid); +int mops_lldp_opt_tlv_TTL (struct mops *mp, int ttl); +int mops_lldp_opt_tlv_vlan (struct mops *mp, int vlan); +int mops_lldp_opt_tlv (struct mops *mp, int type, int len, u_int8_t *value); +int mops_lldp_opt_tlv_end (struct mops *mp) ; + + +/////////////////////////// Services ///////////////////////////// + +// ARP Service: Resolves MAC address of given IP address and interface +int service_arp(char *dev, u_int8_t *ip, u_int8_t *mac); + +int mops_rx_arp (); +void *rx_arp (void *arg); +void got_arp_packet (u_char *args, const struct pcap_pkthdr *header, const u_char *packet); + + +//////////////////// directmops prototypes: /////////////////////////// +int mops_direct(char* dev, int mops_type, char* argstring); + + +//////////////////// automops prototypes: ////////////////////////////////// + + +struct automops * automops_init(); +struct automops * automops_alloc_protocol(); +struct automops * automops_delete_protocol(); +struct automops * automops_search_protocol(); +int automops_dump_all (struct automops* list); +void automops_set_defaults(struct automops * cur); +struct fields * automops_add_field (struct automops *amp); +void automops_field_set_defaults(struct fields *f); +int automops_delete_fields (struct automops *amp); +int mops_str2layers(char *d); +int amp_add_pentry (struct automops *amp, int xntag, char *d); +int amp_add_fentry (struct automops *amp, struct fields *f, int xntag, char *d); +int amp_checkindex(struct automops *amp, int i); +int amp_str2type(char *d); +int amp_type2str(int t, char *s); +struct fields * amp_getfield_byname(struct automops *amp, char *d); +struct automops * amp_getamp_byname(struct automops *head, char *d); +// Creates an independent automops element for mops +// (it will be not part of any linked list so, next=prev=NULL) +struct automops * automops_clone_automops(struct automops * amp); +int amperr2str (int e, char *s); + +// Create automops PDU within *mp based on data in *amp +// +int automops_update (struct mops *mp, struct automops *amp); +void automops_cleanup (struct automops *list); + +char * mapfile (char *fn); + +////////////////////////// XML support ////////////////////////////// +// +// + + +// Simple stack needed to check proper XML nesting. +// The corresponding methods are defined at the bottom. +struct xnstack { + int data[XN_MAX_STACK]; + int cursize; +}; + +enum xml_tags { // mention all allowed tags here! + xml_protocol, + xml_field, + xml_name, + xml_desc, + xml_requires, + xml_conflicts, + xml_payloadtype, + xml_payload, + xml_payloadhex, + xml_index, + xml_longdesc, + xml_type, + xml_constant, + xml_value, + xml_valname, + xml_min, + xml_max, + xml_tlvt, + xml_tlvl, + xml_lshift +}; + + +int xml_check_parent(int t, int p); +int xml_tag2int (char *t); + +int parse_protocol (char *p); +int xml_getnext_tag (char *p, char *t); +int xml_canonic (char *p); +int xml_get_data (char *p, char *t); +int xml_readin (struct automops *amp, char *p); + +void xnstack_init(struct xnstack *s); +int xnstack_get_top(struct xnstack *s); +int xnstack_push(struct xnstack *s, int d); +int xnstack_pop(struct xnstack *s); +int xnstack_size(struct xnstack *s); + +#endif + diff --git a/staging/mops_checksums.c b/staging/mops_checksums.c new file mode 100644 index 0000000..be20f21 --- /dev/null +++ b/staging/mops_checksums.c @@ -0,0 +1,128 @@ +/* + * 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 + * +*/ + +#include "mz.h" +#include "mops.h" + + + + +// -- TOC: -- +// +// u_int16_t mops_sum16 (u_int16_t len, u_int8_t buff[]) +// int mops_get_transport_sum (struct mops *mp) + + +////////////////////////////////////////////////////////////////////////////////// +// +// See also: +// +// RFC1071 - Computing the Internet checksum +// +////////////////////////////////////////////////////////////////////////////////// + + + +// Generic 16-bit checksum code as required for IP and other headers. +// The checksum is calculated over buff[] which is of length len. +// +// RETURN VALUE: The checksum! (Validated - correct!!!) +// +// Example: t16 = mops_sum16 (20, &mp->frame[fp]); +// +u_int16_t mops_sum16 (u_int16_t len, u_int8_t buff[]) +{ + + u_int16_t word16; + u_int32_t sum=0; + u_int16_t i; + + // make 16 bit words out of every two adjacent 8 bit words in the packet and add them up + for (i=0; i<len; i=i+2) + { + word16 =((buff[i]<<8)&0xFF00)+buff[i+1]; + sum = sum + (u_int32_t) word16; + } + + // take only 16 bits out of the 32 bit sum and add up the carries + while (sum>>16) + sum = (sum & 0xFFFF)+(sum >> 16); + + // one's complement the result + sum = ~sum; + + return ((u_int16_t) sum); +} + + + + + +// sets UDP or TCP checksum within mp[]->frame +// TODO: copying the whole segment is ugly and slow; +// make it more efficient and realize it in-place. +// +int mops_get_transport_sum(struct mops *mp) +{ + u_int8_t buf[MAX_PAYLOAD_SIZE]; + u_int16_t len; + int udp_used; + + u_int16_t sum; + + udp_used = mp->use_UDP; // 0 or 1, 0 means TCP + + // IP Pseudoheader (12 Bytes) + mops_hton4(&mp->ip_src, &buf[0]); + mops_hton4(&mp->ip_dst, &buf[4]); + buf[9]=0x00; + + + // Copy segment + if (udp_used) + { + buf[10]=0x11; // proto UDP (17 dec) + len = mp->udp_len; + mops_hton2(&len, &buf[11]); + memcpy(&buf[13], &mp->frame[mp->begin_UDP], len); + // reset checksum to zero + buf[19] = 0x00; + buf[20] = 0x00; + sum = mops_sum16(len+12, buf); + // insert checksum in UDP header (in frame) + mops_hton2 (&sum, &mp->frame[(mp->begin_UDP)+7]); + + } + else + { + buf[10]=0x06; // proto TCP + len = mp->ip_len - mp->ip_IHL; + mops_hton2((u_int16_t*)&len, &buf[11]); + memcpy(&buf[13], &mp->frame[mp->begin_TCP], len); + // reset checksum to zero + buf[29] = 0x00; + buf[30] = 0x00; + sum = mops_sum16(len+12, buf); + // insert checksum in TCP header (in frame) + mops_hton2 (&sum, &mp->frame[(mp->begin_TCP)+17]); + } + + + return 0; +} + diff --git a/staging/mops_dot1Q.c b/staging/mops_dot1Q.c new file mode 100644 index 0000000..1a22439 --- /dev/null +++ b/staging/mops_dot1Q.c @@ -0,0 +1,131 @@ +/* + * 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 + * +*/ + +#include "mz.h" +#include "mops.h" + + +// Remove 802.1Q tags from packet mp +// +// k indicates which tag to be removed (1..n) +// k=0 means: remove all tags! +// +// RETURN VALUE: 1 upon failure, 0 upon success +int mops_dot1Q_remove (struct mops *mp, int k) +{ + int a,b,n; + + if (k==0) { + mp->dot1Q_s=0; + mp->use_dot1Q=0; + return 0; + } + + n = mp->dot1Q_s/4; // n = total number of tags + if (k>n) return 1; + + if (k==1) { // only delete the single tag + mp->dot1Q_s=0; + mp->use_dot1Q=0; + return 0; + } + + // we have more than one tag: + // + if (k==n) { // remove last tag (of several) + mp->dot1Q_s -=4; + return 0; + } + + // remove some non-ending tag: 0, 1, 2, 3 + a = (k-1)*4; // target + b = k*4; // source (what should be copied) + memcpy(&mp->dot1Q[a], &mp->dot1Q[b], (n-k)*4); + mp->dot1Q_s -=4; + + return 0; +} + + +// Unset CFI in tag k where k=1..n +int mops_dot1Q_nocfi (struct mops *mp, int k) +{ + int n; + + n = mp->dot1Q_s/4; // n = total number of tags + if (k>n) return 1; + + mp->dot1Q[((k-1)*4)+2] &=0xef; // unset CFI (0xef = 1110 1111) + return 0; +} + + +// Set CFI in tag k where k=1..n +int mops_dot1Q_cfi (struct mops *mp, int k) +{ + int n; + + n = mp->dot1Q_s/4; // n = total number of tags + if (k>n) return 1; + + mp->dot1Q[((k-1)*4)+2] |=0x10; // set CFI (0x10 = 0001 0000) + return 0; +} + + +// Assign 802.1Q tag with +// v ... VLAN +// c ... CoS +// i ... tag position (starting from zero!) +// +// m ... modification: 1 = dot1Q_s is not changed +// +// NOTE: +// When called from for-loop to add all tags the total size dot1Q_s +// is updated continuously, therefore use m=1. +// +// But when changing a particular tag within an existing 802.1Q stack +// the total number of tags does not change, therefore use m=0. +// +// RETURN VALUE: 0 upon success, 1 upon failure +// +int mops_dot1Q (struct mops *mp, int i, int m, u_int16_t v, u_int16_t c) +{ + u_int8_t *ptr, c8; + + if (i>=MAX_MOPS_DOT1Q_TAGS) return 1; // max number of tags, see definitions in mops.h + if ((v>4095)||(c>7)) return 1; // greater values do not make sense + + // Format: 0x8100 CoS-CFI-VLAN + // where c=CoS, v=VLAN + c8 = (u_int8_t) c; + mp->dot1Q[4*i+0]= 0x81; + mp->dot1Q[4*i+1]= 0x00; + ptr = (u_int8_t*) &v; + mp->dot1Q[4*i+3]=*ptr; + mp->dot1Q[4*i+2]=*(ptr+1); + mp->dot1Q[4*i+2]^= (c8 << 5); + + if (m) { + mp->dot1Q_s=4*(1+i); // NOTE: dot1Q_s = current tag position + 1 + if (mp->dot1Q_s) mp->use_dot1Q = 1; + } + + return 0; +} + diff --git a/staging/mops_ext.c b/staging/mops_ext.c new file mode 100644 index 0000000..0a32a05 --- /dev/null +++ b/staging/mops_ext.c @@ -0,0 +1,466 @@ +/* + * 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 + * +*/ + + + +#include "mz.h" +#include "mops.h" + + + +// Add protocol descriptor of type ptype +// +// Smart behaviour: +// +// - If the desired p_desc has been assigned already, we leave everything +// as it is and return to the calling function (return 0). +// +// - If a p_desc of another type has been already assigned, this function +// clears and frees everything before assigning another p_desc structure. +// +int mops_ext_add_pdesc (struct mops *mp, int ptype) +{ + + // 1. check if desired p_desc is already assigned + if ( (mp->p_desc != NULL) && (mp->p_desc_type == ptype) ) { + return 0; + } + + // 2. remove older p_desc + if (mp->p_desc_type != MOPS_NO_PDESC) { + if (mops_ext_del_pdesc (mp)) return 1; + } + + // 3. allocate and assign a p_desp + switch (ptype) { + case MOPS_ARP: + mp->p_desc = ( MOPS_EXT_ARP ) malloc ( sizeof (struct mops_ext_arp ) ); + mp->p_desc_type = MOPS_ARP; + mops_init_pdesc_arp(mp); + break; + case MOPS_BPDU: + mp->p_desc = ( MOPS_EXT_BPDU ) malloc ( sizeof (struct mops_ext_bpdu ) ); + mp->p_desc_type = MOPS_BPDU; + mops_init_pdesc_bpdu(mp); + break; + case MOPS_CDP: + mp->p_desc = ( MOPS_EXT_CDP ) malloc ( sizeof (struct mops_ext_cdp ) ); + mp->p_desc_type = MOPS_CDP; + mops_init_pdesc_cdp(mp); + break; + case MOPS_DNS: + mp->p_desc = ( MOPS_EXT_DNS ) malloc ( sizeof (struct mops_ext_dns ) ); + mp->p_desc_type = MOPS_DNS; + mops_init_pdesc_dns(mp); + break; + case MOPS_ICMP: + mp->p_desc = ( MOPS_EXT_ICMP ) malloc ( sizeof (struct mops_ext_icmp ) ); + mp->p_desc_type = MOPS_ICMP; + mops_init_pdesc_icmp(mp); + break; + case MOPS_IGMP: + mp->p_desc = ( MOPS_EXT_IGMP ) malloc ( sizeof (struct mops_ext_igmp ) ); + mp->p_desc_type = MOPS_IGMP; + mops_init_pdesc_igmp(mp); + break; + case MOPS_RTP: + mp->p_desc = ( MOPS_EXT_RTP ) malloc ( sizeof (struct mops_ext_rtp ) ); + mp->p_desc_type = MOPS_RTP; + mops_init_pdesc_rtp(mp); + break; + case MOPS_LLDP: + mp->p_desc = ( MOPS_EXT_LLDP ) malloc ( sizeof (struct mops_ext_lldp ) ); + ((struct mops_ext_lldp *)mp->p_desc)->chassis_id = NULL; + ((struct mops_ext_lldp *)mp->p_desc)->port_id = NULL; + ((struct mops_ext_lldp *)mp->p_desc)->optional_tlvs = NULL; + mp->p_desc_type = MOPS_LLDP; + mops_init_pdesc_lldp(mp); + break; + case MOPS_SYSLOG: + mp->p_desc = ( MOPS_EXT_SYSLOG ) malloc ( sizeof (struct mops_ext_syslog ) ); + mp->p_desc_type = MOPS_SYSLOG; + mops_init_pdesc_syslog(mp); + break; + default: + return 1; // unknown protocol + } + + if (mp->p_desc == NULL) { + fprintf (stderr, "mz/mops: could not allocate memory for mops element!\n"); + mp->p_desc_type = MOPS_NO_PDESC; + return 1; + } + + return 0; +} + + +// Delete any protocol descriptor +// 1) Free memory +// 2) Reset p_desc and p_desc_type +// +int mops_ext_del_pdesc (struct mops *mp) +{ + + mp->p_desc_type = MOPS_NO_PDESC; + if (mp->p_desc==NULL) return 1; // already NULL pointer, nothing to free() + + switch (mp->p_desc_type) { + case MOPS_ARP: + free ( (MOPS_EXT_ARP) mp->p_desc ); + break; + case MOPS_BPDU: + free ( (MOPS_EXT_BPDU) mp->p_desc ); + break; + case MOPS_CDP: + free ( (MOPS_EXT_CDP) mp->p_desc ); + break; + case MOPS_DNS: + free ( (MOPS_EXT_DNS) mp->p_desc ); + break; + case MOPS_ICMP: + free ( (MOPS_EXT_ICMP) mp->p_desc ); + break; + case MOPS_IGMP: + free ( (MOPS_EXT_IGMP) mp->p_desc ); + break; + case MOPS_RTP: + free ( (MOPS_EXT_RTP) mp->p_desc ); + break; + case MOPS_LLDP: + if ( ((struct mops_ext_lldp *) mp->p_desc)->chassis_id != NULL) + free ( ((struct mops_ext_lldp *) mp->p_desc)->chassis_id); + if ( ((struct mops_ext_lldp *) mp->p_desc)->port_id != NULL) + free ( ((struct mops_ext_lldp *) mp->p_desc)->port_id); + if ( ((struct mops_ext_lldp *) mp->p_desc)->optional_tlvs != NULL) + free ( ((struct mops_ext_lldp *) mp->p_desc)->optional_tlvs); + free ( (MOPS_EXT_LLDP) mp->p_desc ); + break; + case MOPS_SYSLOG: + free ( (MOPS_EXT_SYSLOG) mp->p_desc ); + break; + case MOPS_NO_PDESC: // already cleared? + break; + + /* nothing */ + } + + mp->p_desc = NULL; + return 0; +} + + +// Create msg based on p_desc data. +// After that call mops_update and the frame is complete. +int mops_ext_update (struct mops *mp) +{ + + switch (mp->p_desc_type) { + case MOPS_ARP: + mops_update_arp(mp); + break; + case MOPS_BPDU: + mops_update_bpdu(mp); + break; + case MOPS_CDP: + break; + case MOPS_DNS: + break; + case MOPS_ICMP: + break; + case MOPS_IGMP: + mops_update_igmp(mp); + break; + case MOPS_RTP: + mops_update_rtp(mp); + break; + case MOPS_LLDP: + mops_update_lldp(mp); + break; + case MOPS_SYSLOG: + break; + case MOPS_NO_PDESC: + return 0; // OK! + break; + default: + return 1; // Unknown value!? + } + + return 0; +} + + +//////// General parameter update functions - modify a single parameter of p_desc structure +// +// 'Standardized' return values: +// +// MOPS_PDESC_LOW Value smaller than lower bound - but will set +// MOPS_PDESC_HIGH Value larger than upper bound - but will set +// +// MOPS_PDESC_OVERFLOW Value exceeded possible range +// +// MOPS_PDESC_NO_MAC Invalid MAC address +// MOPS_PDESC_NO_IP Invalid IP address +// +// MOPS_PDESC_FAILURE Unspecified problem +// MOPS_PDESC_SUCCESS = 0 Value assigned properly +// +// 'Standardized' format: +// +// mops_pdesc_function ( *PDESC_VAR , USER_STRING , LIMITS ) + + + + +// Assign one or more strings to a single string +// Practical example: Concatenate multiple tokens from the CLI +// Will never copy more than 'max' bytes to 'dst' +// +// EXAMPLE: +// +// mops_pdesc_mstrings (clipkt->description, argv, argc, 20); +// +int mops_pdesc_mstrings (char *dst, char* argv[], int argc, int max) +{ + int i; + char tmp[10000]; // should be sufficient for all purposes here + + dst[0]=0x00; + tmp[0]=0x00; + + for (i=0; i<argc; i++) + { // check if next word would exceed tmp: + if ((1+strlen(argv[i]))>(10000-strlen(tmp))) // The '1+' counts for the additional space + return MOPS_PDESC_OVERFLOW; + else + { + strncat(tmp, argv[i], 80); // Enforcing a maximum word length + strcat(tmp, " "); // We get only the tokens, not the spaces inbetween + } + } + + strncpy(dst, tmp, max); + if (strlen(tmp)>max) return MOPS_PDESC_OVERFLOW; + + return MOPS_PDESC_SUCCESS; +} + + + + + +// Assign decimal or hexadecimal u_int8_t value, depending on spec +// spec can be 0=dec or 1=hex +int mops_pdesc_1byte (u_int8_t *dst, char* usr, int spec, int min, int max) +{ + u_int32_t i; + int retval = MOPS_PDESC_SUCCESS; + + if ((max>255)||(min>255)) return MOPS_PDESC_FAILURE; + + if (spec==0) + { + i = (u_int32_t) str2int (usr); + } + else + { + i = (u_int32_t) xstr2int (usr); + } + + if (i>255) return MOPS_PDESC_OVERFLOW; + if (i<min) + retval = MOPS_PDESC_LOW; + else if (i>max) + retval = MOPS_PDESC_HIGH; + + *dst = (u_int8_t) i; + + return retval; +} + + + +// Assign decimal or hexadecimal u_int16_t value, depending on spec +// spec can be 0=dec or 1=hex +int mops_pdesc_2byte (u_int16_t *dst, char* usr, int spec, int min, int max) +{ + u_int32_t i; + int retval = MOPS_PDESC_SUCCESS; + + if ((max>0xffff)||(min>0xffff)) return MOPS_PDESC_FAILURE; + + if (spec==0) + { + i = (u_int32_t) str2int (usr); + } + else + { + i = (u_int32_t) xstr2int (usr); + } + + if (i>0xffff) return MOPS_PDESC_OVERFLOW; + if (i<min) + retval = MOPS_PDESC_LOW; + else if (i>max) + retval = MOPS_PDESC_HIGH; + + *dst = (u_int16_t) i; + + return retval; +} + + +// Assign decimal or hexadecimal u_int32_t value, depending on spec +// spec can be 0=dec or 1=hex +int mops_pdesc_4byte (u_int32_t *dst, char* usr, int spec, unsigned long int min, unsigned long int max) +{ + unsigned long int i; + int retval = MOPS_PDESC_SUCCESS; + + if ((max>0xffffffff)||(min>0xffffffff)) return MOPS_PDESC_FAILURE; + + if (spec==0) + { + i = str2int (usr); + } + else + { + i = xstr2int (usr); + } + + if (i>0xffffffff) return MOPS_PDESC_OVERFLOW; + if (i<min) + retval = MOPS_PDESC_LOW; + else if (i>max) + retval = MOPS_PDESC_HIGH; + + *dst = (u_int32_t) i; + + return retval; +} + + + +// Maps MAC address given in 'usr' (e. g. 00:11:22:aa:bb:cc) into 'dst' +// which is an u_int8_t array. +// +// Returns MOPS_PDESC_FAILURE (=1) upon invalid MAC address +// +int mops_pdesc_mac (u_int8_t *dst, char* usr) +{ + u_int8_t tmp[6]; + + // temporarily backup current value + memcpy ((void*) tmp, (void*) dst, 6); + + if (str2hex_mac (usr, dst)) + { + // restore original value + memcpy ((void*) dst, (void*) tmp, 6); + return MOPS_PDESC_FAILURE; + }; + + return MOPS_PDESC_SUCCESS; +} + + +// Maps an IP address string into an byte-array u_int8_t ip[4] +// Note: the destination is NOT an u_int32_t !!! +int mops_pdesc_ip (u_int8_t *dst, char* usr) +{ + u_int8_t tmp[4]; + int i, len, j=0; + + // Check if format is correct IPv4: + len = strlen(usr); + for (i=0; i<len; i++) + { + if (usr[i]=='.') + j++; + else if (!isdigit(usr[i])) + return MOPS_PDESC_FAILURE; + } + if (j!=3) return MOPS_PDESC_FAILURE; + + // temporarily backup current value + memcpy ((void*) tmp, (void*) dst, 4); + + if (num2hex (usr, dst)!=4) + { + // restore original value + memcpy ((void*) dst, (void*) tmp, 4); + return MOPS_PDESC_FAILURE; + }; + + return MOPS_PDESC_SUCCESS; +} + + + + + + +//////// Initialization functions for each protocol descriptor /////////// +//// Each function expects that an appropriate p_desc is already assigned +//// Also the p_desc_type should be set already. + + + + + + +int mops_init_pdesc_cdp(struct mops *mp) +{ + if (mp->p_desc == NULL) return 1; // p_desc not properly assigned + + + return 0; +} + + +int mops_init_pdesc_dns(struct mops *mp) +{ + if (mp->p_desc == NULL) return 1; // p_desc not properly assigned + + + return 0; +} + + +int mops_init_pdesc_icmp(struct mops *mp) +{ + if (mp->p_desc == NULL) return 1; // p_desc not properly assigned + + + return 0; +} + + + +int mops_init_pdesc_syslog(struct mops *mp) +{ + if (mp->p_desc == NULL) return 1; // p_desc not properly assigned + + return 0; +} + + + + + + diff --git a/staging/mops_ext_arp.c b/staging/mops_ext_arp.c new file mode 100644 index 0000000..79c33a4 --- /dev/null +++ b/staging/mops_ext_arp.c @@ -0,0 +1,239 @@ +/* + * 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 + * +*/ + + + +#include "mz.h" +#include "mops.h" + + +// Initialization function - specify defaults here! +// +int mops_init_pdesc_arp(struct mops *mp) +{ + + struct mops_ext_arp * pd; + + char tmac[6] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + if (mp->p_desc == NULL) return 1; // p_desc not properly assigned + + pd = mp->p_desc; + + pd->hw_type = 0x0001; + pd->pr_type = 0x800; + pd->hw_size = 6; + pd->pr_size = 4; + pd->opcode = 0x0001; // request + memcpy ((void*) pd->sender_mac, (void*) tx.eth_src, 6); + memcpy ((void*) pd->target_mac, (void*) tmac, 6); + memcpy ((void*) pd->sender_ip, (void*) &tx.ip_src, 4); + memcpy ((void*) pd->target_ip, (void*) &tx.ip_src, 4); + + pd->trailer = 18; // default is 18 byte trailer to get a 60 byte packet (instead of only 42) + + return 0; +} + + + + + + + + +///////////////////////////////////////////////////////////////////////////////// +/////////////////////////////// Update functions //////////////////////////////// +// +// **** Here is a summary of mops tool functions: **** +// +// Adds single byte to msg +// int mops_msg_add_byte (struct mops *mp, u_int8_t data); +// +// Adds bit field in *previous* msg-byte using optional left-shift +// int mops_msg_add_field (struct mops *mp, u_int8_t data, int shift); +// +// Adds two bytes in network byte order to msg +// int mops_msg_add_2bytes (struct mops *mp, u_int16_t data); +// +// Adds four bytes in network byte order to msg +// int mops_msg_add_4bytes (struct mops *mp, u_int32_t data); +// +// Adds string of bytes with lenght len +// int mops_msg_add_string (struct mops *mp, u_int8_t *str, int len); +// +// Add counter to message +// int mops_msg_add_counter (struct mops *mp, +// int random, // 1=random, 0=use start/stop/step +// u_int32_t start, // HOST BYTE ORDER +// u_int32_t stop, // HOST BYTE ORDER +// u_int32_t step, // HOST BYTE ORDER +// int bytes // number of bytes used (1|2|4) - selects hton2 or hton4 +// ); +// +// + + +int mops_update_arp(struct mops * mp) +{ + + struct mops_ext_arp * pd; + int i; + + pd = mp->p_desc; + if (pd==NULL) return 1; // no valid pointer to a p_desc + + mp->msg_s = 0; // important! Otherwise the msg would get longer and longer after each call! + + mops_msg_add_2bytes (mp, pd->hw_type); + mops_msg_add_2bytes (mp, pd->pr_type); + mops_msg_add_byte (mp, pd->hw_size); + mops_msg_add_byte (mp, pd->pr_size); + mops_msg_add_2bytes (mp, pd->opcode); + mops_msg_add_string (mp, pd->sender_mac, 6); + mops_msg_add_string (mp, pd->sender_ip, 4); + mops_msg_add_string (mp, pd->target_mac, 6); + mops_msg_add_string (mp, pd->target_ip, 4); + + // Avoid buffer problems: + if (pd->trailer>2000) + { + pd->trailer=2000; + } + + for (i=0; i<pd->trailer; i++) + { + mops_msg_add_byte (mp, 0x00); + } + + return 0; +} + + + +// ARP Service: Resolves MAC address of given IP address and interface +// The result is stored in the last argument 'mac'. +// +// EXAMPLE: +// +// u_int8_t mymac[6]; +// int ip[4]={192,186,0,1}; +// +// service_arp("eth0", ip, mymac); +// /* now mymac should contain the MAC address */ +// +// RETURN VALUE: 0 upon success +// 1 upon error +// +int service_arp(char *dev, u_int8_t *ip, u_int8_t *mac) +{ + int i, devind=0, dev_found=0; + struct mops * mp; + struct mops_ext_arp * pd; + char tmac[6] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + struct arp_table_struct *cur; + + // MOPS framework already available? + if (mp_head==NULL) return 1; + + // Get list index for that device: + for (i=0; i<device_list_entries; i++) { + if (strncmp(device_list[i].dev, dev, 16)==0) { + devind=i; + dev_found=1; + break; + } + } + if (dev_found==0) { + fprintf(stderr, " Warning: Unknown device (sysARP_service)\n"); + return 1; // ERROR: device name not found !!!! + } else { + if (verbose) { + fprintf(stderr, " sysARP_service triggered through interface %s\n", dev); + } + } + + // Look up mops table if already a sysARP packet is available + mp = mops_search_name (mp_head, "sysARP_service"); + if (mp!=NULL) { // entry exists...stop if active! + if (mops_state(mp)==MOPS_STATE_ACTIVE) { + if (verbose==2) fprintf(stderr, " Warning: Stop active MOPS (sysARP_service)\n"); + mops_destroy_thread(mp); + } + } else { + // Allocate a new packet + if ((mp = mops_alloc_packet(mp_head)) == NULL) { + fprintf(stderr, " sysARP_service: ERROR -- cannot allocate MOPS\n"); + return 1; // Problem, memory full? + } else { + strncpy (mp->packet_name, "sysARP_service", 15); + mp->mz_system=1; // indicates MZ private packet + if (mops_ext_add_pdesc (mp, MOPS_ARP)) { + return 1; // error + } + } + } + + // Configure ARP request: + mops_clear_layers(mp, MOPS_ALL); + mops_init_pdesc_arp(mp); + + mp->verbose = 0; + mp->use_ETHER = 1; + mp->count = 1; + mp->eth_type = 0x806; + mz_strncpy(mp->device, dev, 16); + + pd = mp->p_desc; + memcpy ((void*) pd->sender_mac, (void*) device_list[devind].mac_mops, 6); + memcpy ((void*) pd->target_mac, (void*) tmac, 6); + memcpy ((void*) pd->sender_ip, (void*) device_list[devind].ip_mops, 4); + pd->target_ip[0]=ip[0]; + pd->target_ip[1]=ip[1]; + pd->target_ip[2]=ip[2]; + pd->target_ip[3]=ip[3]; + + mops_update_arp(mp); + mops_set_conf(mp); + + // Send ARP request + + if (mops_tx_simple (mp)) { + fprintf(stderr, " Warning: sysARP_service failed!\n"); + return 1; + } + + usleep(100000); // wait 100 ms + // Now hopefully we got an ARP response; + // look up in ARP cache + + cur=device_list[devind].arp_table; + while(cur!=NULL) { + if ((cur->sip[0]==ip[0]) && + (cur->sip[1]==ip[1]) && + (cur->sip[2]==ip[2]) && + (cur->sip[3]==ip[3])) { // entry found! + for (i=0; i<6; i++) { + mac[i] = cur->smac[i]; + } + } + cur=cur->next; + } + + return 0; +} diff --git a/staging/mops_ext_bpdu.c b/staging/mops_ext_bpdu.c new file mode 100644 index 0000000..8cb180d --- /dev/null +++ b/staging/mops_ext_bpdu.c @@ -0,0 +1,242 @@ +/* + * 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 + * +*/ + + + +#include "mz.h" +#include "mops.h" + + +// Initialization function - specify defaults here! +// +int mops_init_pdesc_bpdu(struct mops *mp) +{ + struct mops_ext_bpdu * pd; + int i; + + + if (mp->p_desc == NULL) return 1; // p_desc not properly assigned + pd = mp->p_desc; + + // 1. - Initialize Ethernet header + str2hex("01:80:C2:00:00:00",mp->eth_dst, 6); + + // 2. - Initialize BPDU fields + pd->id = 0; + pd->version = 0; // 0=802.1D, 2=RSTP(802.1w) + pd->bpdu_type = 0x80; // 0=conf, 0x80=topology change, 2=RSTP/MSTP + pd->flags = 0; // X... .... = TCN ACK + // .X.. .... = Agreement + // ..X. .... = Forwarding + // ...X .... = Learning + // .... XX.. = Port Role (e. g. 11=Desgn) + // .... ..X. = Proposal + // .... ...X = TCN + + i = mops_get_device_index(tx.device); + if (i!=-1) { // found + memcpy((void*) &pd->root_id[2], (void*) device_list[i].mac_mops, 6); + memcpy((void*) &pd->bridge_id[2], (void*) device_list[i].mac_mops, 6); + } else { + str2hex("00:00:00:00:00:00", &pd->root_id[2], 6); + str2hex("00:00:00:00:00:00", &pd->bridge_id[2], 6); + } + + pd->root_id[0] = 0x00; + pd->root_id[1] = 0x00; + + pd->bridge_id[0] = 0x00; + pd->bridge_id[1] = 0x00; + + pd->root_pc = 0; // Root Path Cost + pd->port_id = 0; // Port Identifier + pd->message_age = 0; // All timers are multiples of 1/256 sec. Thus times range from 0 to 256 seconds. + pd->max_age = 5120; // 20 seconds + pd->hello_time = 512; + pd->f_delay = 3840; + + str2hex("00:00:00:00:00:00:00:00", pd->trailer, 8); + // either all-zero or 00:00:00:00:02:VLAN(16bit) when PVST+ + pd->rstp = 0; // 1 = RSTP + pd->pvst = 0; // 1=PVST+ , 0 = 802.1D + pd->mstp = 0; // 1 = Multiple Instance STP + + return 0; +} + + +///////////////////////////////////////////////////////////////////////////////// +/////////////////////////////// Update functions //////////////////////////////// +// +// **** Here is a summary of mops tool functions: **** +// +// Adds single byte to msg +// int mops_msg_add_byte (struct mops *mp, u_int8_t data); +// +// Adds bit field in *previous* msg-byte using optional left-shift +// int mops_msg_add_field (struct mops *mp, u_int8_t data, int shift); +// +// Adds two bytes in network byte order to msg +// int mops_msg_add_2bytes (struct mops *mp, u_int16_t data); +// +// Adds four bytes in network byte order to msg +// int mops_msg_add_4bytes (struct mops *mp, u_int32_t data); +// +// Adds string of bytes with lenght len +// int mops_msg_add_string (struct mops *mp, u_int8_t *str, int len); + +int mops_update_bpdu(struct mops * mp) +{ + + struct mops_ext_bpdu * pd; + + pd = mp->p_desc; + if (pd==NULL) return 1; // no valid pointer to a p_desc + mp->msg_s = 0; // important! Otherwise the msg would get longer and longer after each call! + + + // NOTE: the length field does not include the trailer! + if (pd->pvst) + { + str2hex("01:00:0C:CC:CC:CD", mp->eth_dst, 6); + mp->eth_len=50; + str2hex("aa:aa:03:00:00:0c:01:0b",mp->eth_snap, 8); + mp->eth_snap_s = 8; + } + else + { + str2hex("01:80:C2:00:00:00",mp->eth_dst, 6); + mp->eth_len=38; + str2hex("42:42:03",mp->eth_snap, 3); + mp->eth_snap_s = 3; + } + + mops_msg_add_2bytes (mp, pd->id); + mops_msg_add_byte (mp, pd->version); + mops_msg_add_byte (mp, pd->bpdu_type); + + if (pd->bpdu_type & 0x80) // if TCN then don't add more fields + { + if (pd->pvst) mp->eth_len=12; else mp->eth_len=7; + } + else + { + mops_msg_add_byte (mp, pd->flags); + mops_msg_add_string (mp, pd->root_id, 8); + mops_msg_add_4bytes (mp, pd->root_pc); + mops_msg_add_string (mp, pd->bridge_id, 8); + mops_msg_add_2bytes (mp, pd->port_id); + mops_msg_add_2bytes (mp, pd->message_age); + mops_msg_add_2bytes (mp, pd->max_age); + mops_msg_add_2bytes (mp, pd->hello_time); + mops_msg_add_2bytes (mp, pd->f_delay); + } + + // we always add the trailer + mops_msg_add_string (mp, pd->trailer, 8); + + return 0; +} + + + +// Create RID or BID based on priority, ext-sys-id, and MAC address. +// The last parameter selects BID (0) or RID (1) +// +// pri .... 0-15 +// esi .... 0-4095 +// mac .... XX:XX:XX:XX:XX:XX or interface name +// +// NOTE: Invalid parameters will result in default values +// +// RETURN VALUE: Only informational; identifies which parameter +// was errourness, using the following values: +// +// 0 ... all parameters valid +// 1 ... priority exceeded range +// 2 ... ext-sys-id exceeded range +// 3 ... invalid MAC address or invalid interface +// 4 ... other + +int mops_create_bpdu_bid(struct mops * mp, int pri, int esi, char *mac, int bid_or_rid) +{ + int i; + struct mops_ext_bpdu * pd = mp->p_desc; + u_int8_t rid[8]; + u_int16_t p16; + + if ((pri<0)||(pri>15)) return 1; + if ((esi<0)||(esi>4095)) return 2; + + if (mac!=NULL) { + // first check if an interface is specified: + i = mops_get_device_index(mac); + if (i!=-1) { // found + memcpy((void*) &rid[2], (void*) device_list[i].mac_mops, 6); + } + else { // MAC address given? + if (mops_pdesc_mac(&rid[2], mac)) { + return 3; + } + } + } else { // mac==NULL + // use MAC of default interface! + i = mops_get_device_index(tx.device); + if (i!=-1) { // found + memcpy((void*) &rid[2], (void*) device_list[i].mac_mops, 6); + } + else { + str2hex("00:00:00:00:00:00", &rid[2], 6); + return 4; + } + } + + // now prepend pri, esi + + p16 = pri; + p16 <<= 12; + p16 |= esi; + + mops_hton2 (&p16, &rid[0]); + if (bid_or_rid) + memcpy((void*) pd->root_id, (void*) rid, 8); + else + memcpy((void*) pd->bridge_id, (void*) rid, 8); + return 0; +} + + +int mops_create_bpdu_trailer (struct mops * mp, u_int16_t vlan) +{ + struct mops_ext_bpdu * pd = mp->p_desc; + + // PVST+ requires a trailer with either all-zero + // or 00:00:00:00:02:VLAN(16bit) + + // trailer already initialized with zeroes + pd->trailer[0]=0x00; + pd->trailer[1]=0x00; + pd->trailer[2]=0x00; + pd->trailer[3]=0x00; + pd->trailer[4]=0x02; + pd->trailer[5]=0x00; + pd->trailer[6]=0x00; + mops_hton2 (&vlan, &pd->trailer[5]); + + return 0; +} diff --git a/staging/mops_ext_igmp.c b/staging/mops_ext_igmp.c new file mode 100644 index 0000000..3bd83fa --- /dev/null +++ b/staging/mops_ext_igmp.c @@ -0,0 +1,270 @@ +/* + * 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 + * +*/ + +#include "mz.h" +#include "mops.h" +#include "cli.h" + + + +// Initialization function - specify defaults here! +// +int mops_init_pdesc_igmp(struct mops *mp) +{ + struct mops_ext_igmp * pd; + + if (mp->p_desc == NULL) return 1; // p_desc not properly assigned + pd = mp->p_desc; + + pd->version = 2; + pd->type = IGMP_V2_REPORT; + pd->max_resp_code = 0; + pd->sum_false = 0; + pd->group_addr = 0; // TODO: consider initialization with well-known mcast address? + pd->sa_list = NULL; + + return 0; +} + + + + +// IGMPv2 query and report (see RFC 2236) +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Type | Max Resp Time | Checksum | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Group Address | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// +// +// IGMPv1 query and report (see RFC 1112) +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |Version| Type | Unused | Checksum | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Group Address | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// +// Therefore IGMPv1 only uses IGMP_GENERAL_QUERY or IGMP_V1_REPORT and mrt=0. +// +int mops_create_igmpv2 (struct mops *mp, + int override, // normally zero, but if '1' the user want to override defaults + int igmp_type, // IGMP_GENERAL_QUERY, IGMP_GSPEC_QUERY, IGMP_V2_REPORT, IGMP_V1_REPORT, IGMP_LEAVE + int mrt, // max response time (unused == 0 for IGMPv1) + int sum, //-1 means auto-compute, other values means 'use this user-defined value' + u_int32_t group_addr) +{ + struct mops_ext_igmp * pd; + + // --- sanity check params --- + // Do we have a valid pointer? + if (mp->p_desc == NULL) return 1; // p_desc not properly assigned + pd = mp->p_desc; + if (mrt>255) return 1; + if (sum>65535) return 1; + // --------------------------- + + // +++ Set values in pdesc ++++++++++++++++++++++++ + pd->version = 2; + + switch (igmp_type) { + case IGMP_GENERAL_QUERY: + pd->type = 0x11; + pd->group_addr = 0; + pd->max_resp_code = mrt; + break; + case IGMP_GSPEC_QUERY: + pd->type = 0x11; + pd->group_addr = group_addr; + pd->max_resp_code = mrt; + break; + case IGMP_V2_REPORT: + pd->type = 0x16; + pd->group_addr = group_addr; + if (override) pd->max_resp_code = mrt; else pd->max_resp_code = 0; + break; + case IGMP_V1_REPORT: + pd->type = 0x12; + pd->group_addr = group_addr; + if (override) pd->max_resp_code = mrt; else pd->max_resp_code = 0; + break; + case IGMP_LEAVE: + pd->type = 0x17; + pd->group_addr = group_addr; + if (override) pd->max_resp_code = mrt; else pd->max_resp_code = 0; + break; + default: + return 1; // unknown type + } + + if (sum==-1) { + pd->sum_false = 0; + } else { + pd->sum_false = 1; + pd->sum = sum; // mops_update_igmp() will process this! + } + + // ++++++++++++++++++++++++++++++++++++++++++++++++ + + return 0; +} + + + + + + + + + +int mops_update_igmp (struct mops * mp) +{ + struct mops_ext_igmp * pd; + + pd = mp->p_desc; + if (pd==NULL) return 1; // no valid pointer to a p_desc + mp->msg_s = 0; // important! Otherwise the msg would get longer and longer after each call! + u_int16_t sum; + + switch (pd->version) { + + case 1: + break; + + case 2: + // IGMPv2 query and report (see RFC 2236) + // + // 0 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | Type | Max Resp Time | Checksum | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | Group Address | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + mops_msg_add_byte (mp, pd->type); + mops_msg_add_byte (mp, pd->max_resp_code); + if (pd->sum_false) + mops_msg_add_2bytes (mp, pd->sum); // used defined (typically wrong) checksum + else // must be set to zero before checksum computation + mops_msg_add_2bytes (mp, 0x0000); + mops_msg_add_4bytes (mp, pd->group_addr); + if (pd->sum_false==0) { + sum = mops_sum16 (mp->msg_s, mp->msg); + mops_hton2(&sum, &mp->msg[2]); + } + break; + + case 3: + break; + + + default: + return 1; + } + + + + return 0; +} + + + + + + + + +// IGMP messages are encapsulated in IPv4 datagrams, with an IP protocol +// number of 2. Every IGMP message described in this document is sent +// with an IP Time-to-Live of 1, IP Precedence of Internetwork Control +// (e.g., Type of Service 0xc0), and carries an IP Router Alert option +// [RFC-2113] in its IP header. + + + +// +// +// IGMPv3 report message (see RFC 3376) +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Type = 0x22 | Reserved | Checksum | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Reserved | Number of Group Records (M) | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | | +// . . +// . Group Record [1] . +// . . +// | | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | | +// . . +// . Group Record [2] . +// . . +// | | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | . | +// . . . +// | . | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | | +// . . +// . Group Record [M] . +// . . +// | | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// +// +// +// +// IGMPv3 query message (see RFC 3376) +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Type = 0x11 | Max Resp Code | Checksum | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Group Address | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Resv |S| QRV | QQIC | Number of Sources (N) | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Source Address [1] | +// +- -+ +// | Source Address [2] | +// +- . -+ +// . . . +// . . . +// +- -+ +// | Source Address [N] | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// +// +// + +// +// +// +// diff --git a/staging/mops_ext_lldp.c b/staging/mops_ext_lldp.c new file mode 100644 index 0000000..d11fab0 --- /dev/null +++ b/staging/mops_ext_lldp.c @@ -0,0 +1,430 @@ +/* + * 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 + * +*/ + + +#include "mz.h" +#include "mops.h" +#include "cli.h" + + + +// Initialization function - specify defaults here! +// +int mops_init_pdesc_lldp(struct mops *mp) +{ + struct mops_ext_lldp * pd; + int i=0; + + if (mp->p_desc == NULL) return 1; // p_desc not properly assigned + pd = mp->p_desc; + + mp->eth_type = 0x88cc; + str2hex("01:80:c2:00:00:0e", mp->eth_dst, 6); + mp->ndelay.tv_sec = 30; + mp->ndelay.tv_nsec = 0; + + // get interface index for that packet + i = mops_get_device_index(mp->device); + + pd->non_conform = 0; + pd->chassis_id_subtype = 4; // MAC address + if (pd->chassis_id==NULL) pd->chassis_id = malloc(255); + if (pd->chassis_id==NULL) return 1; + memcpy((void*) pd->chassis_id, (void*) device_list[i].mac_mops, 6); + pd->chassis_id_len = 6; + pd->port_id_subtype = 5; // interface name + pd->port_id_len = strnlen(mp->device, 15); + if (pd->port_id==NULL) pd->port_id = malloc(255); + if (pd->port_id==NULL) return 1; + memcpy((void*) pd->port_id, (void*) mp->device, pd->port_id_len); + pd->TTL = 120; + pd->optional_tlvs_s = 0; + if (pd->optional_tlvs==NULL) pd->optional_tlvs = malloc(MAX_LLDP_OPT_TLVS); + if (pd->optional_tlvs == NULL) return 1; + return 0; +} + + + +int mops_update_lldp (struct mops * mp) +{ + struct mops_ext_lldp * pd; + + pd = mp->p_desc; + if (pd==NULL) return 1; // no valid pointer to a p_desc + mp->msg_s = 0; // important! Otherwise the msg would get longer and longer after each call! + + switch (pd->non_conform) { + + case 0: // Derive mandatory TLVs from struct entries and insert optional_tlvs + mp->msg_s += mops_lldp_tlv_chassis(mp->msg, + pd->chassis_id_subtype, + pd->chassis_id_len, + pd->chassis_id); + mp->msg_s += mops_lldp_tlv_port(&mp->msg[mp->msg_s], + pd->port_id_subtype, + pd->port_id_len, + pd->port_id); + mp->msg_s += mops_lldp_tlv_TTL(&mp->msg[mp->msg_s], + pd->TTL); + if (pd->optional_tlvs_s) { + memcpy((void*) &mp->msg[mp->msg_s], + (void*) pd->optional_tlvs, + pd->optional_tlvs_s); + mp->msg_s += pd->optional_tlvs_s; + } + mp->msg_s += mops_lldp_tlv_end(&mp->msg[mp->msg_s]); + break; + + case 1: // User defined ALL TLVs (i. e. ignore struct entries) + if (pd->optional_tlvs_s) { + memcpy((void*) &mp->msg[mp->msg_s], + (void*) pd->optional_tlvs, + pd->optional_tlvs_s); + mp->msg_s += pd->optional_tlvs_s; + } + mp->msg_s += mops_lldp_tlv_end(&mp->msg[mp->msg_s]); + break; + default: + return 1; + } + return 0; +} + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Below are utility functions to creade the LLDPU. From these, the // +// following can be used for the optional part: // +// // +// // +// // +// // + +/* + +int mops_lldp_opt_tlv (struct mops *mp, int type, int len, u_int8_t *value) +int mops_lldp_opt_tlv_chassis (struct mops *mp, int subtype, int len, u_int8_t *cid) +int mops_lldp_opt_tlv_port (struct mops *mp, int subtype, int len, u_int8_t *pid) +int mops_lldp_opt_tlv_TTL (struct mops *mp, int ttl) +int mops_lldp_opt_tlv_vlan (struct mops *mp, int vlan) +int mops_lldp_opt_tlv_end (struct mops *mp) +int mops_lldp_opt_tlv_bad (struct mops *mp, int type, int badlen, int len, u_int8_t *value) +int mops_lldp_opt_tlv_org (struct mops *mp, int oui, int subtype, int len, u_int8_t *inf) + +*/ + + +// // +// // +// // +// // +// // +// // +// // +// // +/////////////////////////////////////////////////////////////////////////////// + + + + + +// Creates a LLDP TLV for a given type number and value string. The result will +// be written into 'tlv'. +// +// NOTE: len must be given and indicates the length of value. +// +// RETURN VALUE: - Total number of bytes of this tlv +// - 0 upon error +// +int mops_lldp_tlv (u_int8_t *tlv, int type, int len, u_int8_t *value) +{ + u_int16_t tl=0, tln=0; + + if ((type>127) || (len>511)) return 0; + + tl = type << 9; + tl |= len; + + tln = htons(tl); + memcpy((void*) tlv, (void*) &tln, 2); + memcpy((void*) &tlv[2], (void*) value, len); + + return len+2; +} + + +// Same as above but **adds the TLVs to the 'pd->optional_tlvs' string.** +// It also checks if MAX_LLDP_OPT_TLVS is exceeded. +// +// NOTE: first argument is a pointer to that mops! +// +// RETURN VALUE: - 0 upon error (no more space) +// - Total number of bytes written +// +int mops_lldp_opt_tlv (struct mops * mp, int type, int len, u_int8_t *value) +{ + struct mops_ext_lldp * pd; + u_int8_t tmp[MAX_LLDP_OPT_TLVS]; // this *must* be sufficient in length + int l; + + if (mp->p_desc == NULL) return 1; // p_desc not properly assigned + pd = mp->p_desc; + + l = mops_lldp_tlv (tmp, type, len, value); + + if ((MAX_LLDP_OPT_TLVS - pd->optional_tlvs_s)< (l+1)) return -1; // not enough space + memcpy((void*) (pd->optional_tlvs + pd->optional_tlvs_s), (void*) tmp, l); + pd->optional_tlvs_s += l; + return l; +} + + +/////////////////////////////////////////////////////////////////////////////// +// // +// // +// // +/////////////////////////////////////////////////////////////////////////////// + + + +// Creates a Chassis ID TLV -- the first mandatory TLV. +// The result will be written into 'tlv'. +// +// RETURN VALUE: - Total number of bytes within tlv +// - 0 upon error +// +int mops_lldp_tlv_chassis (u_int8_t *tlv, int subtype, int len, u_int8_t *cid) +{ + u_int8_t tmp[256]; + + if ((len>255) || (subtype>255)) return 0; + + tmp[0] = (u_int8_t) subtype; + memcpy((void*) (tmp+1), (void*) cid, len); + return mops_lldp_tlv(tlv, 1, len+1, tmp); + +} + +// Same but for optional tlv string +int mops_lldp_opt_tlv_chassis (struct mops *mp, int subtype, int len, u_int8_t *cid) +{ + u_int8_t tmp[256]; + + if ((len>255) || (subtype>255)) return 0; + tmp[0] = (u_int8_t) subtype; + memcpy((void*) (tmp+1), (void*) cid, len); + return mops_lldp_opt_tlv(mp, 1, len+1, tmp); +} + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// // +// // +/////////////////////////////////////////////////////////////////////////////// + + + +// Creates a Port ID TLV -- the second mandatory TLV. +// The result will be written into 'tlv'. +// +// RETURN VALUE: - Total number of bytes within tlv +// - 0 upon error +// +int mops_lldp_tlv_port (u_int8_t *tlv, int subtype, int len, u_int8_t *pid) +{ + u_int8_t tmp[256]; + + if ((len>255) || (subtype>255)) return 0; + + tmp[0] = (u_int8_t) subtype; + memcpy((void*) (tmp+1), (void*) pid, len); + return mops_lldp_tlv(tlv, 2, len+1, tmp); +} + +// Same but for optional tlv string +int mops_lldp_opt_tlv_port (struct mops *mp, int subtype, int len, u_int8_t *pid) +{ + u_int8_t tmp[256]; + + if ((len>255) || (subtype>255)) return 0; + tmp[0] = (u_int8_t) subtype; + memcpy((void*) (tmp+1), (void*) pid, len); + return mops_lldp_opt_tlv(mp, 2, len+1, tmp); +} + + +/////////////////////////////////////////////////////////////////////////////// +// // +// // +// // +/////////////////////////////////////////////////////////////////////////////// + + +// Creates a TTL TLV -- the third mandatory TLV. +// The result will be written into 'tlv'. +// +// RETURN VALUE: - Total number of bytes within tlv +// - 0 upon error +// +int mops_lldp_tlv_TTL (u_int8_t *tlv, int ttl) +{ + u_int16_t ttlh=0, ttln=0; + + if (ttl>0xffff) return 0; + + ttlh = (u_int16_t) ttl; + ttln = htons(ttlh); + + return mops_lldp_tlv(tlv, 3, 2, (u_int8_t*) &ttln); +} + + +// Same but for optional tlv string +int mops_lldp_opt_tlv_TTL (struct mops *mp, int ttl) +{ + u_int16_t ttlh=0, ttln=0; + + if (ttl>0xffff) return 0; + + ttlh = (u_int16_t) ttl; + ttln = htons(ttlh); + + return mops_lldp_opt_tlv(mp, 3, 2, (u_int8_t*) &ttln); +} + +/////////////////////////////////////////////////////////////////////////////// +// // +// // +// // +/////////////////////////////////////////////////////////////////////////////// + + +// Creates an End of LLDPDU TLV -- the last mandatory TLV. +// The result will be written into 'tlv'. +// +// RETURN VALUE: - Total number of bytes within tlv +// - 0 upon error +// +int mops_lldp_tlv_end (u_int8_t *tlv) +{ + tlv[0] = 0x00; + tlv[1] = 0x00; + return 2; +} + +// Same but for optional tlv string +int mops_lldp_opt_tlv_end (struct mops *mp) +{ + struct mops_ext_lldp * pd; + + if (mp->p_desc == NULL) return 1; // p_desc not properly assigned + pd = mp->p_desc; + + if ((MAX_LLDP_OPT_TLVS - pd->optional_tlvs_s) > 2) { + pd->optional_tlvs[pd->optional_tlvs_s++] = 0x00; + pd->optional_tlvs[pd->optional_tlvs_s++] = 0x00; + return 2; + } else + return 0; +} + + +/////////////////////////////////////////////////////////////////////////////// +// // +// // +// // +/////////////////////////////////////////////////////////////////////////////// + + +// Creates a 'bad' LLDP TLV for a given type number and value string. +// The result will be appended into 'pd->optional_tlvs' +// +// NOTE: 'len' must be given and indicates the TRUE length of value. +// 'badlen' can be any number and is used as official length within the TLV +// +// RETURN VALUE: - Total number of bytes within tlv +// - 0 upon error +// +int mops_lldp_opt_tlv_bad (struct mops *mp, + int type, + int badlen, + int len, + u_int8_t *value) +{ + u_int16_t tl=0, tln=0; + u_int8_t tlv[512]; + struct mops_ext_lldp * pd = mp->p_desc; + + if ((type>127) || (len>511) || (badlen>511)) return 0; + if ((MAX_LLDP_OPT_TLVS - pd->optional_tlvs_s) < (len+3)) return 0; + + tl = type << 9; + tl |= badlen; + + tln = htons(tl); + memcpy((void*) tlv, (void*) &tln, 2); + memcpy((void*) &tlv[2], (void*) value, len); + // this detour has historical reasons ;-) + memcpy((void*) (pd->optional_tlvs + pd->optional_tlvs_s), (void*) tlv, len+2); + pd->optional_tlvs += len+2; + + return len+2; +} + + + +// Creates a Organisational-specific TLV -- the second mandatory TLV. +// The result will be appended into 'pd->optional_tlvs' +// +// RETURN VALUE: - Total number of bytes within tlv +// - 0 upon error +// +int mops_lldp_opt_tlv_org (struct mops *mp, + int oui, + int subtype, + int len, + u_int8_t *inf) +{ + u_int8_t tmp[512]; + u_int8_t *x; + u_int32_t oui_n = (u_int32_t) oui; + + if ((len>507) || (subtype>255) || (oui_n>0xffffff)) return 0; + + x = (u_int8_t *) &oui_n; + tmp[0] = *(x+2); + tmp[1] = *(x+1); + tmp[2] = *x; + tmp[3] = (u_int8_t) subtype; + memcpy((void*) (tmp+4), (void*) inf, len); + return mops_lldp_opt_tlv(mp, 127, len+4, tmp); +} + + +int mops_lldp_opt_tlv_vlan (struct mops *mp, + int vlan) +{ + u_int16_t vid; + if (vlan>0xffff) return 0; // yes, we also allow VLAN IDs > 4095 + vid = htons(vlan); + return mops_lldp_opt_tlv_org (mp, 0x80c2, 1, 2, (u_int8_t*) &vid); +} + diff --git a/staging/mops_ext_rtp.c b/staging/mops_ext_rtp.c new file mode 100644 index 0000000..2bc3813 --- /dev/null +++ b/staging/mops_ext_rtp.c @@ -0,0 +1,243 @@ +/* + * 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 + * +*/ + +#include "mz.h" +#include "mops.h" +#include "cli.h" + + + +// Initialization function - specify defaults here! +// +int mops_init_pdesc_rtp(struct mops *mp) +{ + struct mops_ext_rtp * pd; + + if (mp->p_desc == NULL) return 1; // p_desc not properly assigned + pd = mp->p_desc; + + // set RTP defaults + pd->v = 2; + pd->p = 0; + pd->x = 0; + pd->cc = 0; + pd->m = 0; + + pd->pt = 8; // 0=PCMU, 8=PCMA + pd->sqnr = 0; + pd->tst = 0; + pd->tst_inc = 160; + pd->ssrc = mz_rand32(); // Default Mausezahn stream would be 0xCAFEBABE + pd->source = 0; // don't use /dev/dsp (but user may configure source = DSP_SOURCE) + pd->cc_real = 0; + + pd->x_type = 0; // no extension by default + + // General packet parameters + mp->dp = 30000; + mp->sp = 30002; + mp->ndelay.tv_sec = 0; + mp->ndelay.tv_nsec = 20000000; + + memset(&pd->payload, 0x00, MOPS_RTP_MAX_PAYLOAD_SIZE); + + return 0; +} + + +/* + * Standard RTP header according RFC 3550 + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |V=2|P|X| CC |M| PT | sequence number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | timestamp | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | synchronization source (SSRC) identifier | + * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * | contributing source (CSRC) identifiers | + * | .... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * !!! NOTE !!! -- This function should be used only to prepare the RTP + * header once. It does not update dynamic fields. To update dynamic fields + * each time a subsequent RTP packet is sent, use the function + * mops_update_rtp_dynamics(). + * + */ +int mops_update_rtp (struct mops * mp) +{ + struct mops_ext_rtp * pd; + int i,j; + + pd = mp->p_desc; + if (pd==NULL) return 1; // no valid pointer to a p_desc + mp->msg_s = 0; // !! IMPORTANT !! Otherwise the msg would get longer and longer after each call! + + // 1st byte + mops_msg_add_byte (mp, pd->cc); + mops_msg_add_field (mp, pd->v, 6); + mops_msg_add_field (mp, pd->p, 5); + mops_msg_add_field (mp, pd->x, 4); + + // 2nd byte + mops_msg_add_byte (mp, pd->pt); + mops_msg_add_field (mp, pd->m, 7); + + // remaining + mops_msg_add_2bytes (mp, pd->sqnr); + mops_msg_add_4bytes (mp, pd->tst); + mops_msg_add_4bytes (mp, pd->ssrc); + + // Add CSRC list? + if ((j=pd->cc_real)) { + if (j>16) { j=16; pd->cc_real=16; } // silent self healing if desired :-) + for (i=0; i<j; i++) + mops_msg_add_4bytes (mp, pd->csrc[i]); + } + pd->rtp_header_len = 12 + j*4; + + /* + * Add Extension header? + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | defined by profile | length | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | header extension | + * | .... | + */ + + switch (pd->x_type) { + case 0: // none + break; + case 1: // set aero, 8 bytes in total -- TODO -- + break; + case 42: // Mausezahn extension header: + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | MOPS_RTP_EXT_MZID | length=4 | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | TX-timestamp sec | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | TX-timestamp nsec | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | Estimated Peer TX-timestamp sec | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | Estimated Peer TX-timestamp nsec | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + mops_msg_add_2bytes (mp, MOPS_RTP_EXT_MZID); + mops_msg_add_2bytes (mp, 2); + mops_msg_add_4bytes (mp, 0); // only placeholders, must be updated each packet + mops_msg_add_4bytes (mp, 0); // only placeholders, must be updated each packet + mops_msg_add_4bytes (mp, 0); // only placeholders, must be updated each packet + mops_msg_add_4bytes (mp, 0); // only placeholders, must be updated each packet + + pd->rtp_header_len += 20; + break; + default: + return 1; + break; // paranoid? + } + + // Now add the payload + switch (pd->pt) { + case 0: + case 8: + mp->msg_s = 160 + pd->rtp_header_len; // simply set total RTP PDU length (the RTP payload is still undefined) + mp->ndelay.tv_sec = 0; + mp->ndelay.tv_nsec = 20000000; + break; + default: + break; + } + + return 0; +} + + + +// This function directly updates the dynamic RTP fields +// within the mops frame (=be quick here). +// +// This function is typically called from within the transmission loops, +// see e. g. mops_tx_thread_native() +// +// This includes: +// +// - RTP SQNR +// - RTP Timestamp +// - Mausezahn extension header if any +// - The RTP payload +// +int mops_update_rtp_dynamics (struct mops * mp) +{ + struct mops_ext_rtp * pd; + struct timespec ct; + int j, i = mp->begin_MSG; + + pd = mp->p_desc; if (pd==NULL) return 1; + + + // The following variables must be incremented AFTER assignment to frame, + // so the initial values are also used! + // + mops_hton2 (&pd->sqnr, &mp->frame[i+2]); + pd->sqnr++; + + mops_hton4 (&pd->tst, &mp->frame[i+4]); + pd->tst += pd->tst_inc; + + + // Extension header: + // Timestamp must be updated BEFORE assignment to frame + // + switch (pd->x_type) { + case 42: // Mausezahn extension header: Update timestamps + j = i + pd->rtp_header_len; // points to first byte of timestamp of MZ extension header + clock_gettime(CLOCK_MONOTONIC, &ct); + mops_hton4 ((u_int32_t*) &ct.tv_sec, &mp->frame[j-16]); + mops_hton4 ((u_int32_t*) &ct.tv_nsec, &mp->frame[j-12]); +//[TODO] **** estimated peer timestamp **** PSEUDOCODE FOLLOWING: +// if (peer_exists) { +// get_peer_timestamp_estimation(&est); +// mops_hton4 ((u_int32_t*) &est.sec, &mp->frame[j-8]); +// mops_hton4 ((u_int32_t*) &est.nsec, &mp->frame[j-4]); +// } + break; + default: + return 0; + break; + } + + // The pd->payload contains either zeroes or realtime voice data + // The pd->payload is initialized with zeroes and IFF a reading thread + // exists, it may copy voice segments (e. g. from /dev/dsp) to + // pd->payload. + // NOTE that there is NO NEED to protect pd->payload with mutexes, because + // only if the reading thread is finished it (itself!) will call THIS function. + if (pd->source == DSP_SOURCE) { + memcpy((void*) &mp->frame[j], (void*) pd->payload, pd->payload_s); + } + + return 0; +} + diff --git a/staging/mops_ip.c b/staging/mops_ip.c new file mode 100644 index 0000000..46102d7 --- /dev/null +++ b/staging/mops_ip.c @@ -0,0 +1,447 @@ +/* + * 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 + * +*/ + + +#include "mz.h" +#include "mops.h" +#include "cli.h" + + +// PURPOSE +// +// Determine destination MAC address to provide direct or indirect +// delivery of IP packets, depending on which is appropriate. +// +// Doing this, the caller must provide +// 1) A pointer to the interface (within the device_list) +// 2) The destination IP address +// 3) A pointer to the destination MAC address +// +// If a Class D (multicast) address is given, a proper IEEE multicast MAC +// address is derived. +// +// EXAMPLE +// +// u_int8_t ip[4], +// mac[6]; +// +// mops_hton4 (mp->ip_src, ip); +// +// mops_ip_get_dst_mac(&device_list[0], ip, mac); +// +// RETURN VALUES +// +// 0 upon success +// 1 upon error +// +int mops_ip_get_dst_mac(struct device_struct *dev, u_int8_t *ip, u_int8_t *mac) +{ + int i; + u_int8_t dst_net[4]; + + if ((dev==NULL)||(ip==NULL)||(mac==NULL)) return 1; + + // Multicast address? + if ((0xe0 & ip[0]) == 0xe0) { + mac[0] = 0x01; + mac[1] = 0x00; + mac[2] = 0x5e; + mac[3] = ip[1] & 127; + mac[4] = ip[2]; + mac[5] = ip[3]; + return 0; + } + + // Is destination network == local network? + for (i=0; i<4; i++) { + dst_net[i] = ip[i] & (u_int8_t) dev->mask[i]; + } + + if (compare_ip(dst_net, dev->net)==0) { + // dst is on local LAN => resolve MAC! + service_arp(dev->dev, ip, mac); + } else { // dst is on a remote network => use default gw! + for (i=0; i<6; i++) mac[i] = dev->mac_gw[i]; + } + + return 0; +} + + +/////////////////////////////////////////////////////////////////////////////////// +// +// PURPOSE +// +// Accept a DSCP specification as string argument +// and configure the IP-ToS field accordingly. +// +// EXAMPLE STRINGS +// +// AF32 .... specify AF codepoint with class 3 and drop probability 2 +// EF .... specify Expedited Forwarding +// CS7 .... specify Code Selector 7 +// 101110 .... specify the DSCP in binary +// 56 .... specify the DSCP in decimal +// +// RETURN VALUES +// +// -1 general bad argument format +// 0 upon success +// 1 Invalid AF format (Format: AFxy, e. g. af31 or AF23) +// 2 Invalid CS format +// 3 Invalid decimal DSCP value +// +int mops_ip_dscp (struct mops* mp, char *argv) +{ + int i; + char cs[4], ps[4], str[16]; + u_int8_t c=0, p=0, dscp=0; + + if (strlen(argv)==0) return -1; + strncpy(str,argv,15); + + if (strncasecmp(str, "af", 2)==0) // e.g. 'AF32' or 'af41' + { + if (strlen(str)!=4) return 1; // ERROR: Invalid AF codepoint + i=sscanf(str, "%*[afAF]%c%c", cs, ps); + cs[1]=0x00; ps[1]=0x00; + c=(u_int8_t) str2int(cs); + p=(u_int8_t) str2int(ps); + if ((c<1)||(c>4)||(p<1)||(p>3)) return 1; + // Now create correct ToS-byte representation: This is simple, since if a=3 and b=1 + // we have in binary already a=0000 0011 and b=0000 0001 and with bit-shifting we + // get the desired dscp=011 01 000 (the least signfificant three bits are always 0). + c <<=5; + p <<=3; + dscp = c | p; + } + else if (strncasecmp(str, "cs", 2)==0) // e.g. 'CS7' or 'cs4' + { + if (strlen(str)!=2) return 2; // ERROR: Invalid Code Selector + i=sscanf(str, "%*[afAF]%c", cs); + cs[1]=0x00; + c=(u_int8_t) str2int(cs); + if (c>7) return 2; + c <<=5; + dscp = c; + } + else if (mz_strcmp(str, "ef", 2)==0) // e.g. 'ef' or 'EF' + { + dscp = 0xb8; // = DSCP 46 = 101110 00 or 1011 1000 + } + else if (mz_strisbinary(str)==6) // binary, e. g. 101110 + { + for (i=0; i<6; i++) if (str[i]=='1') dscp |= ( 0x01 << (5-i) ); + dscp <<= 2; + } + else if (strlen(str)==2) // decimal DSCP value + { + if ( !(isdigit(str[0])) || !(isdigit(str[1]))) return 3; + dscp = (u_int8_t) str2int(str); + if (dscp>63) return 3; + dscp <<= 2; + } + else return -1; + + // TEST: printf("dscp=%02x\n",dscp); + mp->ip_tos = dscp; + + return 0; +} + + + + + + + + +// +// IP TOS-FIELD FORMAT +// +// MSB LSB +// 0 1 2 3 4 5 6 7 +// +-----+-----+-----+-----+-----+-----+-----+-----+ Note that the bit numbering is usually from right +// | | Del Trp Rel Cst | | to left, but here is the original pic of the RFC +// | PRECEDENCE | TOS | MBZ | 1349. Also here, the MSB is left (strangely bit 0) +// | | | | and the LSB is right (strangely bit 7). +// +-----+-----+-----+-----+-----+-----+-----+-----+ +// +// ARGUMENTS +// if unused +// ipp ... IP Precedence (0..7) or -1 +// tos ... Type of Service (0..15) or -1 +// mbz ... if 1 sets MBZ or 0 +int mops_ip_tos (struct mops* mp, int ipp, int tos, int mbz) +{ + u_int8_t TOS=0; + + if (ipp!=-1) + { + if (ipp>7) return 1; // Invalid IPP value + TOS |= (ipp << 5); + } + + if (tos!=-1) + { + if (tos>15) return 2; // Invalid ToS value + TOS |= (tos << 1); + } + + if (mbz==1) // not used if mbz is either 0 or -1 + { + TOS |= 0x01; // set + } + + mp->ip_tos = TOS; + + return 0; +} + + + +// +// +// =================== ONLY IP OPTION HANDLING FUNCTION BELOW ================ +// +/////////////////////////////////////////////////////////////////////////////// + + + +/////////////////////////////////////////////////////////////////////////////// +// +// There are two cases for the format of an option: +// +// Case 1: A single octet of option-type. +// Case 2: An option-type octet, an option-length octet, and the +// actual option-data octets. +// +// The option-length octet counts the WHOLE number of bytes of the option +// +// The option-type consists of: +// +// +--------+--------+--------+--------+--------+--------+--------+--------+ +// | copied | option class | number (identifies option) | +// | flag | | | +// +--------+-----------------+--------------------------------------------+ +// +// +// The following Internet options are defined in RFC 791: +// +// CLASS NUMBER LENGTH DESCRIPTION +// ----- ------ ------ ----------- +// 0 0 - End of Option list. This option occupies only +// 1 octet; it has no length octet. +// 0 1 - No Operation. This option occupies only 1 +// octet; it has no length octet. +// 0 2 11 Security. Used to carry Security, +// Compartmentation, User Group (TCC), and +// Handling Restriction Codes compatible with DOD +// requirements. +// 0 3 var. Loose Source Routing. Used to route the +// internet datagram based on information +// supplied by the source. +// 0 9 var. Strict Source Routing. Used to route the +// internet datagram based on information +// supplied by the source. +// 0 7 var. Record Route. Used to trace the route an +// internet datagram takes. +// 0 8 4 Stream ID. Used to carry the stream +// identifier. +// 2 4 var. Internet Timestamp. +// +// +// Possible options and associated number in mp->ip_option_used +// +// 1 - Security and handling restrictions (for military applications) +// 2 - Record route +// 4 - Timestamp +// 8 - Loose source routing +// 16 - Strict source routing +// +// + +// *** See RFCs 791, 1071, 1108 *** + +// Remove all options +int mops_ip_option_remove_all (struct mops* mp) +{ + mp->ip_option_used = 0; + mp->ip_option_s = 0; + return 0; +} + + +// Add no-option +int mops_ip_option_nop (struct mops* mp) +{ + + return 0; +} + +// Add end of option list +int mops_ip_option_eol (struct mops* mp) +{ + + return 0; +} + + + +// Add loose source route option +int mops_ip_option_lsr (struct mops* mp) +{ + + return 0; +} + +// Add strict source route option +int mops_ip_option_ssr (struct mops* mp) +{ + + return 0; +} + +// Add record route option +int mops_ip_option_rr (struct mops* mp) +{ + + return 0; +} + +// Add time stamp option +int mops_ip_option_ts (struct mops* mp) +{ + + return 0; +} + + + +// Add security option. +// +// This option provides a way for hosts to send security, compartmentation, +// handling restrictions, and TCC (closed user group) parameters. The format +// for this option is as follows: +// +// +--------+--------+---//---+---//---+---//---+---//---+ +// |10000010|00001011|SSS SSS|CCC CCC|HHH HHH| TCC | +// +--------+--------+---//---+---//---+---//---+---//---+ +// Type=130 Length=11 +// +// Security (S field): 16 bits +// +// Specifies one of 16 levels of security (eight of which are +// reserved for future use). +// +// 00000000 00000000 - Unclassified +// 11110001 00110101 - Confidential +// 01111000 10011010 - EFTO +// 10111100 01001101 - MMMM +// 01011110 00100110 - PROG +// 10101111 00010011 - Restricted +// 11010111 10001000 - Secret +// 01101011 11000101 - Top Secret +// 00110101 11100010 - (Reserved for future use) +// 10011010 11110001 - (Reserved for future use) +// 01001101 01111000 - (Reserved for future use) +// 00100100 10111101 - (Reserved for future use) +// 00010011 01011110 - (Reserved for future use) +// 10001001 10101111 - (Reserved for future use) +// 11000100 11010110 - (Reserved for future use) +// 11100010 01101011 - (Reserved for future use) +// +// +// Compartments (C field): 16 bits +// +// An all zero value is used when the information transmitted is not +// compartmented. Other values for the compartments field may be obtained +// from the Defense Intelligence Agency. +// +// Handling Restrictions (H field): 16 bits +// +// The values for the control and release markings are alphanumeric digraphs +// and are defined in the Defense Intelligence Agency Manual DIAM 65-19, +// "Standard Security Markings". +// +// Transmission Control Code (TCC field): 24 bits +// +// Provides a means to segregate traffic and define controlled communities +// of interest among subscribers. The TCC values are trigraphs, and are available +// from HQ DCA Code 530. +// +// Must be copied on fragmentation. This option appears at most +// once in a datagram. + +int mops_ip_option_sec (struct mops* mp) +{ + + return 0; +} + + +// Add the IP Router Alert Option - a method to efficiently signal +// transit routers to more closely examine the contents of an IP packet. +// See RFC 2113, and FYI also 3175 (RSVP Aggregation), and RFC 5350 +// (new IANA-defined Router Alert Options (RAO)). +// +// The Router Alert option has the following format: +// +// +--------+--------+--------+--------+ +// |10010100|00000100| 2 octet value | +// +--------+--------+--------+--------+ +// +// Type: +// Copied flag: 1 (all fragments must carry the option) +// Option class: 0 (control) +// Option number: 20 (decimal) +// +// Length: 4 +// +// Value: A two octet code with the following values: +// 0 - Router shall examine packet +// 1-65535 - Reserved +// +// RETURN VALUE: 0 upon success +// 1 upon failure +// +int mops_ip_option_ra (struct mops* mp, int value) +{ + int ptr; + u_int16_t val; + + if ((mp==NULL) || (value>0xffff)) return 1; + + val = (u_int16_t) value; + + ptr = mp->ip_option_s; // add option at the end of existing option list (if any) + mp->ip_option_used=20; + + // create option header + mp->ip_option[ptr] = 0x94; + + ptr++; + mp->ip_option[ptr] = 0x04; + + ptr++; + mops_hton2 (&val, &mp->ip_option[ptr]); + ptr+=2; + mp->ip_option_s=4; + + return 0; +} diff --git a/staging/mops_mpls.c b/staging/mops_mpls.c new file mode 100644 index 0000000..5a03a0a --- /dev/null +++ b/staging/mops_mpls.c @@ -0,0 +1,149 @@ +/* + * 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 + * +*/ + + +#include "mz.h" +#include "mops.h" + +// Assigns MPLS tag at position i (starting at zero!) with values: +// +// m ... total number of tags (important to set BoS in last tag) +// Label ... label value +// Exp ... EXP field (typically CoS) +// TTL ... Time To Live +// +// NOTE: Two usage possibilities! +// +// 1.) When called from for-loop to add all tags the total size mpls_s +// is updated continuously and the BoS is set in the last tag. +// Therefore set m = total number of tags! +// +// 2.) But when changing a particular tag within an existing MPLS stack +// the total number of tags does not change, therefore use m=0. +// +// RETURN VALUE: 0 upon success, 1 upon failure +// +int mops_mpls(struct mops *mp, int i, int m, u_int32_t Label, u_int8_t Exp, u_int8_t TTL) +{ + u_int8_t *ptr; + + if ((m) && (i>=m)) return 1; // label index greater than number of labels! + if (Label > 1048575) return 1; + if (Exp > 7) return 1; + + // Create binary tag: Label(20) EXP(3) BoS(1) TTL(8) + Label <<= 4; + ptr = (u_int8_t *) &Label; + mp->mpls[4*i+0] = *(ptr+2); + mp->mpls[4*i+1] = *(ptr+1); + mp->mpls[4*i+2] = *(ptr+0); + Exp <<= 1; + mp->mpls[4*i+2] |= Exp; + mp->mpls[4*i+3] = TTL; + + if ((m) && (i==(m-1))) // reached last tag! + { + mp->mpls[4*i+2] |= 0x01; // set BoS in last tag + mp->mpls_s =4*m; + mp->use_MPLS = 1; + if ( (mp->eth_type != 0x8847) && (mp->eth_type != 0x8848) ) + { + mp->eth_type_backup = mp->eth_type; + } + mp->eth_type = 0x8847; + } + return 0; +} + + +// Remove MPLS tags from packet mp +// +// j indicates which tag to be removed (1..n) +// j=0 means: remove all tags! +// +// RETURN VALUE: 1 upon failure, 0 upon success +int mops_mpls_remove (struct mops *mp, int j) +{ + int a, b, k; + + + if (j==0) // remove all tags + { + if (mp->use_MPLS) + { + mp->mpls_s=0; + mp->use_MPLS=0; + mp->eth_type = mp->eth_type_backup; // restore original ethertype + return 0; + } + else + return 1; + } + + k = mp->mpls_s/4; + if (j>k) return 1; // The packet only consists of k tag(s) + + if (k==1) // only delete the single tag + { + mp->mpls_s=0; + mp->use_MPLS=0; + mp->eth_type = mp->eth_type_backup; // restore original ethertype + return 0; + } + + // if we got here we have more than one tag: + + if (j==k) // remove last tag (of several) + { + mp->mpls_s -=4; + return 0; + } + + // remove some non-ending tag: 0, 1, 2, 3 + a = (j-1)*4; // target + b = j*4; // source (what should be copied) + memcpy(&mp->mpls[a], &mp->mpls[b], (k-j)*4); + mp->mpls_s -=4; + return 0; +} + + +// Set BOS in tag k where k=1..n +int mops_mpls_bos (struct mops *mp, int k) +{ + int n; + + n = mp->mpls_s/4; // n = total number of tags + if (k>n) return 1; + + mp->mpls[(k-1)*4+2] |= 0x01; + return 0; +} + + +// Unset BOS in tag k where k=1..n +int mops_mpls_nobos (struct mops *mp, int k) +{ + int n; + + n = mp->mpls_s/4; // n = total number of tags + if (k>n) return 1; + + mp->mpls[(k-1)*4+2] &= 0xfe; + return 0; +} diff --git a/staging/mops_sequence.c b/staging/mops_sequence.c new file mode 100644 index 0000000..32e7895 --- /dev/null +++ b/staging/mops_sequence.c @@ -0,0 +1,303 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 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 + * +*/ + + +#include "mz.h" +#include "cli.h" +#include "mops.h" +#include "llist.h" + + +///////////////////// TOC ///////////////////// +// +// int mops_delete_sequence (char *name) +// struct mz_ll * mops_create_sequence (char *name) +// int mops_dump_sequence (char* str) +// int mops_add_packet_to_sequence (struct mz_ll *seq, struct mops *mp) +// int mops_add_delay_to_sequence (struct mz_ll *seq, struct timespec *t) +// int mops_delete_packet_from_pseq (struct mz_ll *seq, int index) +// int stop_sequence (char *name) +// int stop_all_sequences () + + +// delete one sequence element (from the global packet_sequence list) +// which must be specified by its name +// +int mops_delete_sequence (char *name) +{ + struct mz_ll *v; + + v = mz_ll_search_name (packet_sequences, name); + if (v==NULL) return 1; // name not found + + if (v->state) return 2; // sequence is currently active! + + if (mz_ll_delete_element (v)) + return -1; // cannot delete head element! + return 0; +} + + + +struct mz_ll * mops_create_sequence (char *name) +{ + struct mz_ll *cur; + struct pseq *seq; + int i; + + cur = mz_ll_create_new_element(packet_sequences); + if (cur==NULL) return NULL; + strncpy(cur->name, name, MZ_LL_NAME_LEN); + // add data + cur->data = (struct pseq*) malloc (sizeof(struct pseq)); + // initialize data + seq = (struct pseq*) cur->data; + seq->count = 0; + for (i=0; i<MAX_PACKET_SEQUENCE_LEN; i++) { + seq->packet[i] = NULL; // pointer to the packets + seq->gap[i].tv_sec = 0; + seq->gap[i].tv_nsec = 0; + } + return cur; +} + + + +// PURPOSE: dumps all sequence objects line-by-line +// +// ARGUMENTS: Caller must provide a pointer to a string of size MZ_LL_NAME_LEN+(MAX_PACKET_SEQUENCE_LEN*6) +// (recommendation: 512 bytes !) +// +// RETURN VALUE: 0 if list is finished, 1 otherwise +// +// EXAMPLE: char str[512]; +// while (mops_dump_sequence(str) +// printf("%s\n", str); +// +int mops_dump_sequence (char* str) +{ + static int init=0; + static struct mz_ll *cur; + struct pseq *seq; + struct mops *pkt; + + char tmp[256], t[16]; + int i, c; + + tmp[0]='\0'; + + if (init==-1) { // last turn said stop now! + init=0; + return 0; + } + + if (init==0) { + cur=packet_sequences->next; + if (cur==NULL) { + str[0]='\0'; + return 0; + } + init=1; + } + + seq = (struct pseq*) cur->data; + if (seq==NULL) { + init=-1; + sprintf(str, "(no sequences found)"); + return 1; + } + + c = seq->count; // amount of currently stored packets in this sequence object + + // create string with all packet IDs: + for (i=0; i<c; i++) { + pkt = seq->packet[i]; + if (pkt == NULL) break; + snprintf(t, 15, "%i", pkt->id); + if (strnlen(tmp,256)>249) break; + strncat(tmp, t, 6); + if (i<c-1) strncat(tmp,", ", 2); + } + + snprintf(str, 512, "%s {%s}", cur->name, tmp); + + cur=cur->next; + if (cur==packet_sequences) init=-1; // stop next turn! + return 1; +} + + +// finds next free slot in sequence seq and adds packet mp +// +// RETURN VALUE: 0 upon success +// -1 failure: array full +// -2 failure: cannot add packets with infinite count +// +int mops_add_packet_to_sequence (struct mz_ll *seq, struct mops *mp) +{ + struct pseq *cur; + int i; + + if (seq==NULL) return 1; + + // don't add packets with count=0 + if (mp->count==0) return -2; + + cur = (struct pseq*) seq->data; + if (cur->count >= MAX_PACKET_SEQUENCE_LEN) return -1; // packet array full! + for (i=0; i<MAX_PACKET_SEQUENCE_LEN; i++) { + if (cur->packet[i]==NULL) { // found empty slot + cur->packet[i]=mp; + cur->count++; + return 0; + } + } + return 1; // never reach here +} + + +// adds the given delay 't' to the last packet in the sequence's pseq +// +// NOTE: return index number of pseq where delay had been added +// or upon failure: -1 if there is no packet yet defined +// -2 if array is full +int mops_add_delay_to_sequence (struct mz_ll *seq, struct timespec *t) +{ + struct pseq *cur; + int i; + + if (seq==NULL) return 1; + + cur = (struct pseq*) seq->data; + i = cur->count; + if (i>= MAX_PACKET_SEQUENCE_LEN) return -2; // packet array full! + + cur->gap[i-1].tv_sec = t->tv_sec; + cur->gap[i-1].tv_nsec = t->tv_nsec; + + return i-1; // note: is -1 if there is no packet yet (count=0) +} + + +// Deletes packet and associated delay from a pseq for given index +// If index == -1 then the last packet/delay is removed +// +// NOTE: index range is {1..count} +// +// RETURN VALUES: 0 upon success +// 1 upon failure +// 2 upon failure, index too big +// +int mops_delete_packet_from_pseq (struct mz_ll *seq, int index) +{ + struct pseq *cur; + int i; + + if (seq==NULL) return 1; + cur = (struct pseq*) seq->data; + if (cur->count==0) return 1; // list is empty, nothing to delete + if (index>cur->count) return 2; + if ((index==0) || (index<-1)) return 1; // total invalid index values + if (index==-1) { // remove last element + cur->packet[cur->count-1]=NULL; + cur->gap[cur->count-1].tv_sec=0; + cur->gap[cur->count-1].tv_nsec=0; + } else { + for (i=index-1; i<(cur->count-1); i++) { + cur->packet[i] = cur->packet[i+1]; + cur->gap[i].tv_sec = cur->gap[i+1].tv_sec; + cur->gap[i].tv_nsec=cur->gap[i+1].tv_nsec; + } + } + cur->count--; + return 0; +} + + +int mops_delete_all_packets_from_pseq (struct mz_ll *seq) +{ + struct pseq *cur; + int i; + + if (seq==NULL) return 1; + cur = (struct pseq*) seq->data; + if (cur->count==0) return 1; // list is empty, nothing to delete + // DELETE ALL: + cur->count = 0; + for (i=0; i<MAX_PACKET_SEQUENCE_LEN; i++) { + cur->packet[i] = NULL; // pointer to the packets + cur->gap[i].tv_sec = 0; + cur->gap[i].tv_nsec = 0; + } + return 0; +} + + + +// Stops an active sequence and sets all involved packets from state SEQACT to CONFIG. +// +// RETURN VALUE: 0 upon success +// 1 if sequence does not exist +// 2 if sequence is not actice +int stop_sequence (char *name) +{ + struct mz_ll *v; + struct pseq *cur; + int i; + + v = mz_ll_search_name (packet_sequences, name); + if (v==NULL) return 1; // name not found + if (!v->state) return 2; // sequence is not currently active! + + // now stop thread: + pthread_cancel(v->sequence_thread); + + // reset packet states: + cur = (struct pseq*) v->data; + for (i=0; i<cur->count; i++) + cur->packet[i]->state=MOPS_STATE_CONFIG; + + // reset sequence state: + v->state = 0; + return 0; +} + + +// runs through 'packet_sequences' and cancels all active sequences +// (i. e. stops threads and sets states appropriately) +// +// Comment: It might seem a bit inefficient to call 'stop_sequence' for the +// detailed work, but this is the more safer way and it is fast enough. +// +// RETURN VALUE: Number of stopped sequences. +// +int stop_all_sequences () +{ + struct mz_ll *cur=packet_sequences->next; + int i=0; + + while (cur!=packet_sequences) { + if (cur!=packet_sequences) { // just for safety + if (stop_sequence(cur->name)==0) i++; + } + cur=cur->next; + } + + return i; +} + diff --git a/staging/mops_tcp.c b/staging/mops_tcp.c new file mode 100644 index 0000000..4d788bd --- /dev/null +++ b/staging/mops_tcp.c @@ -0,0 +1,154 @@ +/* + * 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 + * +*/ + + +#include "mz.h" +#include "mops.h" +#include "cli.h" + +// Calculates the number of TCP transmissions based on SQNR range +u_int32_t mops_tcp_complexity_sqnr (struct mops * mp) +{ + u_int32_t a,b,t,result; + + a = mp->tcp_seq_start; + b = mp->tcp_seq_stop; + t = mp->tcp_seq_delta; + + if (!t) return 1; // delta set to zero means no range + + if (a<b) // regular case + result = ceill ((b-a)/t); + else // range wraps around + result = ceill (((0xffffffff-a) + b)/t); + + return result; +} + + +// Calculates the number of TCP transmissions based on SQNR range +u_int32_t mops_tcp_complexity_acknr (struct mops * mp) +{ + u_int32_t a,b,t,result; + + a = mp->tcp_ack_start; + b = mp->tcp_ack_stop; + t = mp->tcp_ack_delta; + + if (!t) return 1; // delta set to zero means no range + + if (a<b) // regular case + result = ceill ((b-a)/t); + else // range wraps around + result = ceill (((0xffffffff-a) + b)/t); + + return result; +} + + + + +// *****TODO: TCP Options ****** + +// Remove all options +int mops_tcp_option_remove_all (struct mops* mp) +{ + + return 0; +} + + +// Prints current flag settings in the provided string 'str'. +// NOTE that str must be at least 32 bytes! +// *** BETTER USE 64 bytes (for future compatibility) *** +// +int mops_tcp_flags2str (struct mops* mp, char *str) +{ + if (mp==NULL) { + sprintf(str, "(no valid mops)\n"); + return 1; + } + + sprintf(str, "%s-%s-%s-%s-%s-%s-%s-%s", + (mp->tcp_ctrl_CWR) ? "CRW" : "---", + (mp->tcp_ctrl_ECE) ? "ECE" : "---", + (mp->tcp_ctrl_URG) ? "URG" : "---", + (mp->tcp_ctrl_ACK) ? "ACK" : "---", + (mp->tcp_ctrl_PSH) ? "PSH" : "---", + (mp->tcp_ctrl_RST) ? "RST" : "---", + (mp->tcp_ctrl_SYN) ? "SYN" : "---", + (mp->tcp_ctrl_FIN) ? "FIN" : "---"); + + return 0; +} + +// Add TCP options +// +// TODO: currently all params are ignored and a default option combination is added. +// +int mops_tcp_add_option (struct mops* mp, + int mss, + int sack, + int scale, + u_int32_t tsval, + u_int32_t tsecr) +{ + + u_int8_t tcp_default_options[] = { + 0x02, 0x04, 0x05, 0xac, // MSS + 0x04, 0x02, // SACK permitted + 0x08, 0x0a, 0x19, 0x35, 0x90, 0xc3, 0x00, 0x00, 0x00, 0x00, // Timestamps + 0x01, // NOP + 0x03, 0x03, 0x05 // Window Scale 5 + }; + + + /* Kind: 8 + Length: 10 bytes + + +-------+-------+---------------------+---------------------+ + |Kind=8 | 10 | TS Value (TSval) |TS Echo Reply (TSecr)| + +-------+-------+---------------------+---------------------+ + 1 1 4 4 + * + * The Timestamps option carries two four-byte timestamp fields. The + * Timestamp Value field (TSval) contains the current value of the + * timestamp clock of the TCP sending the option. + * + * The Timestamp Echo Reply field (TSecr) is only valid if the ACK bit + * is set in the TCP header; if it is valid, it echos a times- tamp + * value that was sent by the remote TCP in the TSval field of a + * Timestamps option. When TSecr is not valid, its value must be zero. + * The TSecr value will generally be from the most recent Timestamp + * option that was received; however, there are exceptions that are + * explained below. + * + * A TCP may send the Timestamps option (TSopt) in an initial <SYN> + * segment (i.e., segment containing a SYN bit and no ACK bit), and + * may send a TSopt in other segments only if it re- ceived a TSopt in + * the initial <SYN> segment for the connection. + * + */ + + memcpy((void*) mp->tcp_option, (void*) tcp_default_options, 20); + mp->tcp_option_s = 20; + mp->tcp_option_used = 1; + + return 0; +} + diff --git a/staging/mops_threads.c b/staging/mops_threads.c new file mode 100644 index 0000000..69893db --- /dev/null +++ b/staging/mops_threads.c @@ -0,0 +1,639 @@ +/* + * 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 + * +*/ + +#include "mz.h" +#include "mops.h" +#include "cli.h" +#include "llist.h" + + + +void mops_set_active (struct mops *mp) +{ + pthread_mutex_lock (& (mp->mops_mutex) ); + mp->state = MOPS_STATE_ACTIVE; + pthread_mutex_unlock (& (mp->mops_mutex) ); +} + +void mops_set_seqact (struct mops *mp) +{ + pthread_mutex_lock (& (mp->mops_mutex) ); + mp->state = MOPS_STATE_SEQACT; + pthread_mutex_unlock (& (mp->mops_mutex) ); +} + + +void mops_set_conf (struct mops *mp) +{ + pthread_mutex_lock (& (mp->mops_mutex) ); + mp->state = MOPS_STATE_CONFIG; + pthread_mutex_unlock (& (mp->mops_mutex) ); +} + + +int mops_is_active (struct mops *mp) +{ + int i=0; + pthread_mutex_lock (& (mp->mops_mutex) ); + if (mp->state == MOPS_STATE_ACTIVE) i=1; + pthread_mutex_unlock (& (mp->mops_mutex) ); + return i; +} + +// Returns 1 if the packet is in any running state +// such as MOPS_STATE_ACTIVE or MOPS_STATE_SEQACT +int mops_is_any_active (struct mops *mp) +{ + int i=0; + pthread_mutex_lock (& (mp->mops_mutex) ); + if (mp->state > MOPS_STATE_CONFIG) i=1; + pthread_mutex_unlock (& (mp->mops_mutex) ); + return i; +} + + +int mops_is_seqact (struct mops *mp) +{ + int i=0; + pthread_mutex_lock (& (mp->mops_mutex) ); + if (mp->state == MOPS_STATE_SEQACT) i=1; + pthread_mutex_unlock (& (mp->mops_mutex) ); + return i; +} + + + +// return mops state (0=MOPS_STATE_NULL, 1=MOPS_STATE_INIT, 2=MOPS_STATE_CONFIG, 3=MOPS_STATE_ACTIVE, 4=MOPS_STATE_SEQACT) +int mops_state (struct mops *mp) +{ + int i=0; + pthread_mutex_lock (& (mp->mops_mutex) ); + i = mp->state; + pthread_mutex_unlock (& (mp->mops_mutex) ); + return i; +} + + +int mops_tx_simple (struct mops *mp) +{ + + if (mops_is_active(mp)) { + return 3; + } + + if (mp->interval_used) { + if ( pthread_create( &(mp->interval_thread), NULL, mops_interval_thread, mp) ) { + mp->interval_used=1; // 1 means interval only configured + return 1; // Error creating thread + } + } else // normal packet train + if ( pthread_create( &(mp->mops_thread), NULL, mops_tx_thread_native, mp) ) { + return 1; // Error creating thread + } + + return 0; +} + + +// Starts a packet sequence. +// +// RETURN VALUES: 0 upon success +// 1 failure: packet not in CONFIG state +// 2 failure: packet has infinite count +int mops_tx_sequence (struct mz_ll *seq) +{ + struct pseq *cur; + int i; + + // verify 1) that all packets are in config state + // 2) and have finite count: + cur = (struct pseq*) seq->data; + for (i=0; i<cur->count; i++) { + if (cur->packet[i]->state!=MOPS_STATE_CONFIG) return 1; + if (cur->packet[i]->count==0) return 2; + } + + // Set all packets in this sequence into state SEQACT: + for (i=0; i<cur->count; i++) + mops_set_seqact (cur->packet[i]); + + if ( pthread_create( &(seq->sequence_thread), NULL, mops_sequence_thread, seq) ) { + return 3; // Error creating thread + } + seq->state=1; + return 0; +} + + +// This is the sequence sending thread +void *mops_sequence_thread (void *arg) +{ + struct mz_ll *seq = (struct mz_ll*) arg; + struct pseq *cur; + int i; + + cur = (struct pseq*) seq->data; + + // Send one packet after each other, possibly with gaps inbetween: + for (i=0; i<cur->count; i++) { + mops_tx_thread_native (cur->packet[i]); + // if gap exists... + if ((cur->gap[i].tv_sec) || (cur->gap[i].tv_nsec)) { + nanosleep(&cur->gap[i], NULL); //...apply it. + } + } + + // Finally: + // 1) reset all packets into config state + for (i=0; i<cur->count; i++) + cur->packet[i]->state=MOPS_STATE_CONFIG; + // 2) join to main + pthread_exit(NULL); + // 3) set sequence state to inactive (=0) + seq->state=0; + + return NULL; +} + +// This is the interval management thread which starts +// packet transmission threads by itself. +// +// Note how this works: After the while statement below we have actually +// two threads, mops_tx_thread_native (sending the packet) and mops_interval_thread which +// starts mops_tx_thread_native every mp->interval. If mp->interval is smaller than +// mp->delay (and mp->count > 1) then multiple transmission threads will be active at the +// same time which is usually not what the user wants. We do not catch this case here +// but the user interface should do that (it is done in 'cmd_packet_interval'). +// +void *mops_interval_thread (void *arg) +{ + struct mops *mp = (struct mops*) arg; + + mp->interval_used=2; // 2 means active interval + while (1) { + if ( pthread_create( &(mp->mops_thread), NULL, mops_tx_thread_native, mp) ) { + mp->interval_used=1; + pthread_exit(NULL); + } + nanosleep(&mp->interval, NULL); + } + + pthread_exit(NULL); // hmm...does this make sense? + return NULL; +} + + +// General MOPS sending thread using packet sockets. +// +void *mops_tx_thread_native (void *arg) +{ + struct mops *mp = (struct mops*) arg; + struct mops_ext_rtp * pd; + int ps, i, n=0; + u_int8_t DA[4]; + // Local vars are faster -------------------------- + struct timespec tv; + register int infinity, devind; + int ip_src_isrange = mp->use_IP & mp->ip_src_isrange; + int ip_dst_isrange = mp->use_IP & mp->ip_dst_isrange; + int sp_isrange = (mp->use_UDP | mp->use_TCP) & mp->sp_isrange; + int dp_isrange = (mp->use_UDP | mp->use_TCP) & mp->dp_isrange; + int ip_src_israndom = mp->use_IP & mp->ip_src_israndom; + int sp_isrand = (mp->use_UDP | mp->use_TCP) & mp->sp_isrand; + int dp_isrand = (mp->use_UDP | mp->use_TCP) & mp->dp_isrand; + + + u_int32_t + ip_src_start = mp->ip_src_start, + ip_src_stop = mp->ip_src_stop, + ip_dst_start = mp->ip_dst_start, + ip_dst_stop = mp->ip_dst_stop, + tcp_seq_delta = mp->tcp_seq_delta, + tcp_seq_range = 0, + tcp_ack_delta = mp->tcp_ack_delta, + tcp_ack_range = 0, + tcp_ack_count = 0, + tcp_seq_count = 0; + + int + sp_start = mp->sp_start, + dp_start = mp->dp_start, + sp_stop = mp->sp_stop, + dp_stop = mp->dp_stop; + + int + rtp_mode = 0; // RTP not used + + int + fragsize = 0, + frag_overlap = 0, + fragptr = 0, + offset = 0, + offset_delta = 0, + begin_ip_payload = 0, + ip_payload_s = 0, + original_msg_s = 0, + whats_used = 0; // store use_UDP or use_TCP here to clean up packet parameters finally + char + original_msg[MAX_MOPS_MSG_SIZE+1], // temporary buffer when fragmentation is needed + ip_payload[MAX_MOPS_MSG_SIZE+1]; // temporary buffer when fragmentation is needed + + + // ------------------------------------------------- + + + ///////////////////////////// + // NOTE: If packet is part of a sequence, then this function is already part of a sequence thread + // and all packets are already in state SEQACT. Otherwise we set the packet in state ACTIVE. + if (!mops_is_seqact(mp)) + mops_set_active (mp); + ///////////////////////////// + + + // infinite or not? Count up or down? + if (mp->count == 0) { + infinity = 1; + mp->cntx = 0; + } + else { + infinity = 0; + mp->cntx = mp->count; // count down + } + + // Which delay? + tv.tv_sec = mp->ndelay.tv_sec; + tv.tv_nsec = mp->ndelay.tv_nsec; + + // Which interface? + for (i=0; i<device_list_entries; i++) { + if (strncmp(device_list[i].dev, mp->device, 15)==0) break; + } + devind=i; + + // Packet socket already existing and valid? + ps = device_list[devind].ps; // the packet socket + if (ps<0) goto FIN; + + // Automatic direct or indirect delivery for IP packets? + if ((mp->use_IP) && (mp->auto_delivery_off == 0)) { + if (mp->ip_dst_isrange) + mops_hton4(&mp->ip_dst_start, DA); + else + mops_hton4(&mp->ip_dst, DA); + + mops_ip_get_dst_mac(&device_list[devind], DA, mp->eth_dst); + } + + + // Impossible settings + if (((ip_src_isrange) && (ip_src_israndom)) || + ((sp_isrand) && (sp_isrange)) || + ((dp_isrand) && (dp_isrange))) { + fprintf(stderr, "[ERROR] (mops_tx_thread_native) -- conflicting requirements: both range and random!\n"); + goto FIN; + } + + // Initialize start values when ranges have been defined + if (ip_src_isrange) mp->ip_src = mp->ip_src_start; + if (ip_dst_isrange) mp->ip_dst = mp->ip_dst_start; + if (sp_isrange) mp->sp = mp->sp_start; + if (dp_isrange) mp->dp = mp->dp_start; + if (tcp_seq_delta) { + tcp_seq_range = mops_tcp_complexity_sqnr(mp); + mp->tcp_seq = mp->tcp_seq_start; + tcp_seq_count = tcp_seq_range; + } + if (tcp_ack_delta) { + tcp_ack_range = mops_tcp_complexity_acknr(mp); + mp->tcp_ack = mp->tcp_ack_start; + tcp_ack_count = tcp_ack_range; + } + + // RTP special message treatment + if (mp->p_desc_type == MOPS_RTP) { + pd = mp->p_desc; + if (pd==NULL) return NULL; + if (pd->source == DSP_SOURCE) + rtp_mode = 2; // dsp payload + else + rtp_mode = 1; // zero payload + + mops_update_rtp (mp); // initialize RTP packet here + } + + // TODO: VLAN, MPLS - ranges + + // + // ---------------------- The holy transmission loop ---------------- // + // + + // Update whole packet (once before loop!) + mops_ext_update (mp); + mops_update(mp); + + + // Check if IP fragmentation is desired. + // If yes, set local 'fragsize' and 'begin_ip_payload' pointer. + if (mp->ip_fragsize) { + if (mp->use_IP) { + fragsize = mp->ip_fragsize; + frag_overlap = mp->ip_frag_overlap; + offset = mp->ip_frag_offset; + offset_delta = (fragsize-frag_overlap)/8; + if (mp->use_UDP) { + begin_ip_payload = mp->begin_UDP; + whats_used = 1; + } else if (mp->use_TCP) { + begin_ip_payload = mp->begin_TCP; + whats_used = 2; + } else { + begin_ip_payload = mp->begin_MSG; + whats_used = 0; + } + ip_payload_s = mp->frame_s - begin_ip_payload; + memcpy((void*) original_msg, (void*) mp->msg, mp->msg_s); + original_msg_s = mp->msg_s; + memcpy((void*) ip_payload, (void*) &mp->frame[begin_ip_payload], ip_payload_s); + } + } + + + goto START; // looks like a dirty hack but reduces a few cpu cycles each loop + + do { + INLOOP: + nanosleep(&tv, NULL); // don't apply this before first and after last packet. + START: + + // +++++++++++++++++++++++++++++++++++ + + + // ------ IP fragmentation required? ------------------------------------------------------ + // + // Basic idea: At this point we assume that all updates have been already applied + // so mp->frame contains a valid packet. But now we do the following: + // + // 1. Determine first byte after end of IP header (IP options may be used) [done above] + // 2. Store the 'IP payload' in the temporary buffer 'ip_payload' [done above] + // 3. Create a new IP payload but take only the first fragsize bytes out of 'ip_payload' + // 4. This new IP payload is copied into mp->msg + // 5. Set the IP parameters: MF=1, offset=0 + // 6. Call mops_update() and send the packet + // 7. offset = offset + fragsize/8 + // 8. Increment the IP identification number + // 9. Repeat this until the last fragment is reached. For the last fragment + // set the flag MF=0. + // 10. Restore the original IP parameters (use_UDP or use_TCP) + if (fragsize) { + mp->use_UDP=0; + mp->use_TCP=0; + fragptr=0; // NOTE: by intention we do not set mp->ip_frag_offset to 0 here !!! The user knows what she does! + mp->ip_flags_MF=1; + mp->ip_id++; // automatically wraps around correctly (u_int16_t) + // send all fragments except the last one: + while(fragptr+fragsize < ip_payload_s) { + memcpy((void*) mp->msg, (void*) ip_payload+fragptr, fragsize); + mp->msg_s = fragsize; + mops_update(mp); + n = write(ps, mp->frame, mp->frame_s); + if (n!=mp->frame_s) { + fprintf(stderr, "ERROR: Could not send IP fragment through interface %s\n", mp->device); + // LOG error msg + goto FIN; + } + fragptr+=fragsize; + mp->ip_frag_offset += offset_delta; + } + // send last fragment: + mp->ip_flags_MF=0; + memcpy((void*) mp->msg, (void*) ip_payload+fragptr, ip_payload_s-fragptr); + mp->msg_s = ip_payload_s-fragptr; + mops_update(mp); + n = write(ps, mp->frame, mp->frame_s); + if (n!=mp->frame_s) { + fprintf(stderr, "ERROR: Could not send IP fragment through interface %s\n", mp->device); + // LOG error msg + goto FIN; + } + + // -- restore original mops parameters -- + switch (whats_used) { + case 1: mp->use_UDP = 1; break; + case 2: mp->use_TCP = 1; break; + } + memcpy((void*) mp->msg, (void*) original_msg, original_msg_s); + mp->msg_s = original_msg_s; + mp->ip_frag_offset=offset; + goto NEXT; + } + + // -- send unfragmented packets here: -- + n = write(ps, mp->frame, mp->frame_s); + if (n!=mp->frame_s) { + fprintf(stderr, "ERROR: Could not send packet through interface %s\n", mp->device); + // LOG error msg + goto FIN; + } + + NEXT: + + /* [ RTP TODO: ] Use another thread reading from /dev/dsp and signalling us to continue! + * It should work like this: (pseudocode below) + * + * if (rtp_mode == DSP_SOURCE) { + * pthread_cond_wait ( &mycond, &mymutex ); // wait until pthread condition is signaled + * // now, frame should contain 160 bytes from /dev/dsp + * goto INLOOP; + * } + * + * The reading thread will do something like this: (again fuzzy code only) + * + * loop: + * read(fd, pd->rtp_payload, 160); // this takes 20 msec anyway + * mops_update_rtp_dynamics (mp); // also updates dynamic header fields + * pthread_cond_broadcast (&mycond); // wake up TX thread + * goto loop; + * + * See also + * http://www.oreilly.de/catalog/multilinux/excerpt/ch14-05.htm + * + * NOTE that we must not reach nanosleep below because the 20 msec delay is + * done implicitely by reading 160 bytes from /dev/dsp + */ + + switch (rtp_mode) { + case 1: // dummy payload => segmentation delay is controlled by nanosleep below! + mops_update_rtp_dynamics (mp); + break; + case 2: // await data from /dev/dsp => segmentation delay is controlled by a reading thread! + /* pthread_cond_wait ( &mycond, &mymutex ); // wait until pthread condition is signaled + * // now, frame should contain 160 bytes from /dev/dsp + * goto INLOOP; + */ + break; + default: + // no RTP, continue as usual + break; + } + + + // +++++++++++++++++++++++++++++++++++ + // + // *** begin of modifiers -- order is important! *** *************** // + // + if (tcp_seq_delta) { + if (--tcp_seq_count) { + mp->tcp_seq += tcp_seq_delta; + mops_update(mp); + goto INLOOP; + } else { + tcp_seq_count = tcp_seq_range; + mp->tcp_seq = mp->tcp_seq_start; + mops_update(mp); + } + } + + if (tcp_ack_delta) { + if (--tcp_ack_count) { + mp->tcp_ack += tcp_ack_delta; + mops_update(mp); + goto INLOOP; + } else { + tcp_ack_count = tcp_ack_range; + mp->tcp_ack = mp->tcp_ack_start; + mops_update(mp); + } + } + + if (ip_src_isrange) { + if (++mp->ip_src > ip_src_stop) { + mp->ip_src = ip_src_start; + mops_update(mp); + } + else { + mops_update(mp); + goto INLOOP; + } + } + + if (ip_src_israndom) { + mp->ip_src = 0x01000001 + (u_int32_t) ( ((float) rand()/RAND_MAX)*0xE0000000); //latter is 224.0.0.0 + } + + if (ip_dst_isrange) { + if (++mp->ip_dst > ip_dst_stop) { + mp->ip_dst = ip_dst_start; + if (mp->auto_delivery_off == 0) { + mops_hton4(&mp->ip_dst, DA); + mp->eth_dst[0] = 0x01; + mp->eth_dst[1] = 0x00; + mp->eth_dst[2] = 0x5e; + mp->eth_dst[3] = DA[1] & 127; + mp->eth_dst[4] = DA[2]; + mp->eth_dst[5] = DA[3]; + } + mops_update(mp); + } + else { + if (mp->auto_delivery_off == 0) { + mops_hton4(&mp->ip_dst, DA); + mp->eth_dst[0] = 0x01; + mp->eth_dst[1] = 0x00; + mp->eth_dst[2] = 0x5e; + mp->eth_dst[3] = DA[1] & 127; + mp->eth_dst[4] = DA[2]; + mp->eth_dst[5] = DA[3]; + } + mops_update(mp); + goto INLOOP; + } + } + + if (dp_isrange) { + if (++mp->dp > dp_stop) { + mp->dp = dp_start; + mops_update(mp); + } + else { + mops_update(mp); + goto INLOOP; + } + } + + if (dp_isrand) { + mp->dp = (u_int16_t) ( ((float) rand()/RAND_MAX)*0xffff); + } + + + if (sp_isrange) { + if (++mp->sp > sp_stop) { + mp->sp = sp_start; + mops_update(mp); + } + else { + mops_update(mp); + goto INLOOP; + } + } + + if (sp_isrand) { + mp->sp = (u_int16_t) ( ((float) rand()/RAND_MAX)*0xffff); + } + + + // *** end of modifiers ******************************************** // + if (infinity) { + mp->cntx++; // count up + goto INLOOP; + } + } while (--mp->cntx); + + FIN: + if (!mops_is_seqact(mp)) { + // only [change state and close thread] if packet is NOT part of a sequence. + // If the packet is part of a sequence then THIS function is already part of + // a sequence thread and it will be closed in 'mops_sequence_thread'. + mops_set_conf (mp); + pthread_exit(NULL); + } + return NULL; +} + + + + + +int mops_destroy_thread (struct mops *mp) +{ + int r=1; + + if (mp->interval_used==2) { + pthread_cancel(mp->interval_thread); + mp->interval_used=1; + r=0; + } + + if (mops_is_active(mp)) { + pthread_cancel(mp->mops_thread); + pthread_mutex_destroy(& mp->mops_mutex); + mops_set_conf(mp); + r=0; + } + + return r; +} diff --git a/staging/mops_tools.c b/staging/mops_tools.c new file mode 100644 index 0000000..022f45b --- /dev/null +++ b/staging/mops_tools.c @@ -0,0 +1,259 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008,2009 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 + * +*/ + + +#include "mz.h" +#include "mops.h" + +// Inserts value in 'flag' (up to 7 bits are useful) into the target +// with an optional left-shift. For example if flag contains a 4-bit value +// and should be placed within the target in bit positions 3-6 like: +// +// 7 6 5 4 3 2 1 0 +// +--+--+--+--+--+--+--+--+ +// | | FLAGS | | | | +// +--+--+--+--+--+--+--+--+ +// +// then simply call: +// +// (void) mops_flags ( &target, &flag, 3 ); +// +// Note that shift=0 means no shift. +inline void mops_flags (u_int8_t *target, u_int8_t *flag, int shift) +{ + *target |= (*flag << shift); +} + + + +inline void mops_hton2 (u_int16_t *host16, u_int8_t *net16) +{ + char *x; + + x = (char*) host16; + + *(net16++) = *(x+1); + *net16 = *x; +} + + +inline void mops_hton4 (u_int32_t *host32, u_int8_t *net32) +{ + char *x; + + x = (char*) host32; + + *(net32++) = *(x+3); + *(net32++) = *(x+2); + *(net32++) = *(x+1); + *(net32) = *x; +} + + + + +// returns new counter index for given packet +// or -1 if all counters used already +int mops_get_counter (struct mops *mp) +{ + int i=0; + + while (mp->counter[i].offset) + { + i++; + if (i==MAX_MOPS_COUNTERS_PER_PACKET) // exceeded range + return -1; + } + return i; +} + + +// Adds single byte to msg +int mops_msg_add_byte (struct mops *mp, u_int8_t data) +{ + mp->msg[mp->msg_s++] = data; + return 0; +} + + +// Adds bit field in *previous* msg-byte using optional left-shift +int mops_msg_add_field (struct mops *mp, u_int8_t data, int shift) +{ + mp->msg[mp->msg_s -1] |= (data << shift); + return 0; +} + + +// Adds two bytes in network byte order to msg +int mops_msg_add_2bytes (struct mops *mp, u_int16_t data) +{ + char *x; + x = (char*) &data; + mp->msg[mp->msg_s++] = *(x+1); + mp->msg[mp->msg_s++] = *(x); + return 0; +} + + +// Adds four bytes in network byte order to msg +int mops_msg_add_4bytes (struct mops *mp, u_int32_t data) +{ + char *x; + x = (char*) &data; + mp->msg[mp->msg_s++] = *(x+3); + mp->msg[mp->msg_s++] = *(x+2); + mp->msg[mp->msg_s++] = *(x+1); + mp->msg[mp->msg_s++] = *(x); + return 0; +} + +// Adds string of bytes with lenght len +int mops_msg_add_string (struct mops *mp, u_int8_t *str, int len) +{ + memcpy((void *) &mp->msg[mp->msg_s], (void *) str, len); + mp->msg_s += len; + + return 0; +} + + + +// Add counter to message +int mops_msg_add_counter (struct mops *mp, + int random, // 1=random, 0=use start/stop/step + u_int32_t start, // HOST BYTE ORDER + u_int32_t stop, // HOST BYTE ORDER + u_int32_t step, // HOST BYTE ORDER + int bytes // number of bytes used (1|2|4) - selects hton2 or hton4 + ) +{ + + int i; + + // check if unsupported byte count + if ( (bytes!=1) && + (bytes!=2) && + (bytes!=4) ) + return 1; + + // get new counter + i = mops_get_counter(mp); + if (i==-1) return 1; + + // configure counter values + mp->counter[i].offset = mp->msg_s; + mp->counter[i].random = random; + mp->counter[i].start = start; + mp->counter[i].stop = stop; + mp->counter[i].step = step; + mp->counter[i].bytes = bytes; + mp->counter[i].cur = start; + mp->counter[i].use = 1; + + + // configure first pointer value + switch (bytes) + { + case 1: + mops_msg_add_byte(mp, (u_int8_t) start); + break; + case 2: + mops_msg_add_2bytes(mp, (u_int16_t) start); + break; + case 4: + mops_msg_add_4bytes(mp, start); + break; + default: // never be reached + return 1; + } + + return 0; +} + + + +// Compares two IP addresses byte by byte +// returns 0 if identical, 1 if different +// +// Note that this works independent of endianess +// as long as both addresses have same endianess. +// +int compare_ip (u_int8_t *ip1, u_int8_t *ip2) +{ + if (*ip1 != *ip2) return 1; + if (*(ip1+1) != *(ip2+1)) return 1; + if (*(ip1+2) != *(ip2+2)) return 1; + if (*(ip1+3) != *(ip2+3)) return 1; + + return 0; +} + + +// Compares two MAC addresses byte by byte +// returns 0 if identical, 1 if different +int compare_mac (u_int8_t *mac1, u_int8_t *mac2) +{ + if (*mac1 != *mac2) return 1; + if (*(mac1+1) != *(mac2+1)) return 1; + if (*(mac1+2) != *(mac2+2)) return 1; + if (*(mac1+3) != *(mac2+3)) return 1; + if (*(mac1+4) != *(mac2+4)) return 1; + if (*(mac1+5) != *(mac2+5)) return 1; + + return 0; +} + + +// Converts a 'struct timespec' value into a human readable string +// This stringt is written into 'str' which must be at least a 32 byte +// array. +int timespec2str(struct timespec *t, char *str) +{ + unsigned int d=0, h, m, s; + + // zero delay + if ((t->tv_sec==0) && (t->tv_nsec==0)) { + sprintf(str, "(none)"); + return 0; + } + + h = t->tv_sec/3600; + m = (t->tv_sec - h*3600)/60; + s = t->tv_sec - h*3600 - m*60; + + if (h>24) { + d = h/24; + h = h - d*24; + sprintf(str, "%u days %02u:%02u:%02u", d, h, m, s); + return 0; + } + + if (h|m) + sprintf(str, "%02u:%02u:%02u", h, m, s); // ignore nanoseconds if delay is in order of hours + else if (s) + sprintf(str, "%u%s sec", s, (t->tv_nsec>1000000) ? "+" : ""); + else if (t->tv_nsec>1000000) + sprintf(str, "%u msec", (unsigned int) t->tv_nsec/1000000); + else if (t->tv_nsec>1000) + sprintf(str, "%u usec", (unsigned int) t->tv_nsec/1000); + else + sprintf(str, "%lu nsec", t->tv_nsec); + + return 0; +} + diff --git a/staging/mops_update.c b/staging/mops_update.c new file mode 100644 index 0000000..4fa1b3a --- /dev/null +++ b/staging/mops_update.c @@ -0,0 +1,422 @@ +/* + * 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: -- +// int mops_update (stuct mops *mp) + + +#include "mz.h" +#include "mops.h" + + + +// This is the very basic MOPS update function. It simply updates the whole +// MOPS frame specified by the pointer mp. If you only want to update specific +// details then please see the other related specialized functions which are +// more effcient. +// +int mops_update (struct mops *mp) +{ + int + i, // the standard loop variable; outside a loop fully undetermined! + t, // temp + fp=0; // frame pointer; always points to NEXT byte + + char *x; + u_int8_t t8=0; // temp 8 bits + u_int16_t t16; // temp 16 bits + + u_int8_t ip_pseudo_header[12]; + + + // set MAC addresses? + if (mp->use_ETHER) + { + for (i=0; i<6; i++) + { + mp->frame[i] = mp->eth_dst[i]; + mp->frame[i+6] = mp->eth_src[i]; + } + fp = 12; // next byte + } + + + + // VLAN tags? + if (mp->use_dot1Q) + { + t = mp->dot1Q_s; + for (i=0; i<t; i++) + { + mp->frame[fp++] = mp->dot1Q[i]; + } + } + + + + // Standard Ethernet or SNAP? (SNAP includes 802.3, see comments in mops.h) + if (mp->use_SNAP) // note that if use_SNAP is set, then the 'else if' below is ignored! + { + // 802.3 length + x = (char*) &mp->eth_len; + mp->frame[fp++] = *(x+1); + mp->frame[fp++] = *x; + // SNAP + t = mp->eth_snap_s; + for (i=0; i<t; i++) + { + mp->frame[fp++] = mp->eth_snap[i]; + } + } + else if (mp->use_ETHER) // add TYPE field (note the ELSE IF here!) + { + // EtherType + x = (char*) &mp->eth_type; + mp->frame[fp++] = *(x+1); + mp->frame[fp++] = *x; + } + // alternatively the user specified whole raw frame + // + // + // + // MPLS? + if (mp->use_MPLS) + { + t = mp->mpls_s; + for (i=0; i<t; i++) + { + mp->frame[fp++] = mp->mpls[i]; + } + } + + + + + // IP? + if (mp->use_IP) + { + mp->begin_IP = fp; // marks byte position of IP header within frame + + // ----- 1st row: ----- + // + mp->frame[fp] = (mp->ip_version << 4); // version + mp->frame[fp++] |= mp->ip_IHL; // IHL (user value - corrected at end of function if required) + mp->frame[fp++] = mp->ip_tos; // ToS + mops_hton2 ( &mp->ip_len, &mp->frame[fp] ); // Total Length (user value - corrected at end of function if required) + fp+=2; + + // ----- 2nd row: ----- + // + mops_hton2 ( &mp->ip_id, &mp->frame[fp] ); // Fragment Identification + fp+=2; + + mops_hton2 ( &mp->ip_frag_offset, &mp->frame[fp] ); // Fragment Identification + // set flags: + if (mp->ip_flags_MF) mp->frame[fp] |= 0x20; else mp->frame[fp] &= 0xDF; // More Frag + if (mp->ip_flags_DF) mp->frame[fp] |= 0x40; else mp->frame[fp] &= 0xBF; // Don't Frag + if (mp->ip_flags_RS) mp->frame[fp] |= 0x80; else mp->frame[fp] &= 0x7F; // reserved + fp+=2; + + // ----- 3rd row: ----- + + mp->frame[fp++] = mp->ip_ttl; // TTL + mp->frame[fp++] = mp->ip_proto; // Protocol + mops_hton2 ( &mp->ip_sum, &mp->frame[fp] ); // Checksum (user value - corrected at end of function if required) + fp+=2; + + // ----- 4th and 5th row: ----- + // + mops_hton4 ( &mp->ip_src, &mp->frame[fp] ); // SA + fp+=4; + mops_hton4 ( &mp->ip_dst, &mp->frame[fp] ); // DA + fp+=4; + + // ----- options ----- + // + if (mp->ip_option_used) + { + t = mp->ip_option_s; + for (i=0; i<t; i++) + { + mp->frame[fp++] = mp->ip_option[i]; + } + } + } + + + + + // UDP? + if (mp->use_UDP) + { + mp->begin_UDP = fp; // marks byte position of UDP header within frame + + mops_hton2 ( &mp->sp, &mp->frame[fp] ); // Source Port + fp+=2; + mops_hton2 ( &mp->dp, &mp->frame[fp] ); // Destination Port + fp+=2; + mops_hton2 ( &mp->udp_len, &mp->frame[fp] ); // Length (user value - corrected at end of function if required) + fp+=2; + mops_hton2 ( &mp->udp_sum, &mp->frame[fp] ); // CheckSum (user value - corrected at end of function if required) + fp+=2; + } + + + + // TCP? + if (mp->use_TCP) + { + mp->begin_TCP = fp; // marks byte position of TCP header within frame + + // ----- 1st row: ----- + // + mops_hton2 ( &mp->sp, &mp->frame[fp] ); // Source Port + fp+=2; + mops_hton2 ( &mp->dp, &mp->frame[fp] ); // Destination Port + fp+=2; + + // ----- 2nd and 3rd row: ----- + // + mops_hton4 ( &mp->tcp_seq, &mp->frame[fp] ); // SQNR + fp+=4; + mops_hton4 ( &mp->tcp_ack, &mp->frame[fp] ); // ACKNR + fp+=4; + + // ----- 4th row: ----- + // +// t16 = (mp->tcp_offset<<12) + (mp->tcp_res<<8); // Data Offset (HLEN) and 4 reserved bits + t16 = mp->tcp_res<<8; // Data Offset (HLEN) and 4 reserved bits + // (user value - corrected at end of function if required) + // + if (mp->tcp_ctrl_CWR) t16 |= 0x0080; else t16 &= 0xff7f; // URG Flag + if (mp->tcp_ctrl_ECE) t16 |= 0x0040; else t16 &= 0xffbf; // URG Flag + if (mp->tcp_ctrl_URG) t16 |= 0x0020; else t16 &= 0xffdf; // URG Flag + if (mp->tcp_ctrl_ACK) t16 |= 0x0010; else t16 &= 0xffef; // ACK Flag + if (mp->tcp_ctrl_PSH) t16 |= 0x0008; else t16 &= 0xfff7; // PSH Flag + if (mp->tcp_ctrl_RST) t16 |= 0x0004; else t16 &= 0xfffb; // RST Flag + if (mp->tcp_ctrl_SYN) t16 |= 0x0002; else t16 &= 0xfffd; // SYN Flag + if (mp->tcp_ctrl_FIN) t16 |= 0x0001; else t16 &= 0xfffe; // FIN Flag + + mops_hton2 ( &t16, &mp->frame[fp] ); // copy HLEN, reserved bits, and flags to frame + fp+=2; + + + mops_hton2 ( &mp->tcp_win, &mp->frame[fp] ); // Window + fp+=2; + + // ----- 5th row: ----- + // + mops_hton2 ( &mp->tcp_sum, &mp->frame[fp] ); // Checksum + fp+=2; + + mops_hton2 ( &mp->tcp_urg, &mp->frame[fp] ); // Urgent pointer + fp+=2; + + + // ----- options: ----- + // + if (mp->tcp_option_used) { + t=mp->tcp_option_s; + for (i=0; i<t; i++) { + mp->frame[fp++] = mp->tcp_option[i]; + } + } + } + + // Eventually the payload: + if ((t = mp->msg_s)) + { + mp->begin_MSG = fp; + for (i=0; i<t; i++) { + mp->frame[fp++] = mp->msg[i]; + } + } + + mp->frame_s = fp; // finally set the total frame length + + + ////////////////////////////////////////////////////////////// + // Protect TX subsystem from too short or long packets // + // TODO: Consider to support mops-specific limits + // (which are itself limited by these global limits) + if (fp < min_frame_s) + mp->frame_s = min_frame_s; + else + if (fp > max_frame_s) + mp->frame_s = max_frame_s; + // // + ////////////////////////////////////////////////////////////// + + + + + //////////////////////////////////////////////////////////////////////////////// + // + // Now update "derivable" fields if required: + // + // IP: ip_IHL, ip_len, ip_sum + // UDP: udp_len, udp_sum + // TCP: tcp_offset, tcp_sum + // + // + if (mp->use_IP) + { + fp = mp->begin_IP; // marks byte position of IP header within frame + + /// HLEN + if (!mp->ip_IHL_false) { // user has NOT set an own header length + t8 = 5; + if (mp->ip_option_used) { // add option length if option exists + t8 += mp->ip_option_s/4; + } + t8 &= 0x0f; // set most significant 4 bits to zero because reserved for IP version + mp->frame[fp] |= t8; + } + + /// LEN + if (!mp->ip_len_false) { // user has NOT set an own total length + t16 = mp->frame_s-fp; + mops_hton2 ( &t16, &mp->frame[fp+2] ); // Calculated total Length + } + + /// SUM + if (!mp->ip_sum_false) { // user has NOT set an own header checksum + mp->frame[fp+10]=0x00; + mp->frame[fp+11]=0x00; + t16 = mops_sum16 (t8*4, &mp->frame[fp]); + mops_hton2 ( &t16, &mp->frame[fp+10] ); // Checksum (user value - corrected at end of function if required) + } + } + + + if (mp->use_UDP) + { + fp = mp->begin_UDP; // marks byte position of UDP header within frame + + /// LEN + if (!mp->udp_len_false) { // user has NOT set an own total length + t16 = mp->frame_s-fp; + mops_hton2 ( &t16, &mp->frame[fp+4] ); // Calculated total Length + } + + /// SUM + // + // The pseudo header conceptually prefixed to the UDP header contains the + // source address, the destination address, the protocol, and the UDP + // length. [RFC 768] + // + // 0 7 8 15 16 23 24 31 + // +--------+--------+--------+--------+ + // | source address | + // +--------+--------+--------+--------+ + // | destination address | + // +--------+--------+--------+--------+ + // | zero |protocol| UDP length | + // +--------+--------+--------+--------+ + // + // + if (!mp->udp_sum_false) // user has NOT set an own checksum + { + // Create IP pseudoheader + memcpy(&ip_pseudo_header[0], &mp->frame[mp->begin_IP+12], 4); // copy SA to pseudoheader + memcpy(&ip_pseudo_header[4], &mp->frame[mp->begin_IP+16], 4); // copy DA to pseudoheader + ip_pseudo_header[8]=0x00; + ip_pseudo_header[9]=mp->ip_proto; + memcpy(&ip_pseudo_header[10], &mp->frame[fp+4], 2); // copy UDP length to pseudoheader + + mp->frame[fp+6]=0x00; // set checksum to 0x0000 + mp->frame[fp+7]=0x00; + + t = 12+mp->frame_s-fp; // udp datagram length (including 12 byte pseudoheader) + + // Pad one extra byte if length is odd, and append the + // pseudoheader at the end of mp->frame (only for checksum computation) + if (t%2) + { + t++; + mp->frame[mp->frame_s]=0x00; + memcpy(&mp->frame[mp->frame_s+1], ip_pseudo_header, 12); + } + else + memcpy(&mp->frame[mp->frame_s], ip_pseudo_header, 12); + + t16 = mops_sum16 (t, &mp->frame[fp]); + mops_hton2 ( &t16, &mp->frame[fp+6] ); + } + } + + + + + if (mp->use_TCP) + { + fp = mp->begin_TCP; // marks byte position of TCP header within frame + + /// OFFSET (=HLEN) + if (!mp->tcp_offset_false) // user has NOT set an own header length + { + t8 = 5; + if (mp->tcp_option_used) {// add option length if option exists + t8 += mp->tcp_option_s/4; + } + t8 <<=4; + mp->frame[fp+12] |= t8; + } + + // The TCP checksum is calculated similarily as the UDP checksum (see above). + // (The TCP length is needed instead of the UDP length of course, although + // the TCP length is not part of the header) + // + if (!mp->tcp_sum_false) { + // Create IP pseudoheader + memcpy(&ip_pseudo_header[0], &mp->frame[mp->begin_IP+12], 4); // copy SA to pseudoheader + memcpy(&ip_pseudo_header[4], &mp->frame[mp->begin_IP+16], 4); // copy DA to pseudoheader + ip_pseudo_header[8]=0x00; + ip_pseudo_header[9]=mp->ip_proto; + mp->tcp_len = mp->frame_s-fp; // TCP segment length + t16 = htons (mp->tcp_len); + memcpy(&ip_pseudo_header[10], &t16, 2); // copy TCP length to pseudoheader + + mp->frame[fp+16]=0x00; // set checksum to 0x0000 + mp->frame[fp+17]=0x00; + + t = mp->tcp_len+12; // TCP segment length plus pseudoheader length + + // Pad one extra byte if length is odd, and append the + // pseudoheader at the end of mp->frame (only for checksum computation) + if (t%2) { + t++; + mp->frame[mp->frame_s]=0x00; + memcpy(&mp->frame[mp->frame_s+1], ip_pseudo_header, 12); + } + else + memcpy(&mp->frame[mp->frame_s], ip_pseudo_header, 12); + + t16 = mops_sum16 (t, &mp->frame[fp]); + mops_hton2 ( &t16, &mp->frame[fp+16] ); + } + } + + + return 0; +} + + diff --git a/staging/mopsrx_arp.c b/staging/mopsrx_arp.c new file mode 100644 index 0000000..0aac152 --- /dev/null +++ b/staging/mopsrx_arp.c @@ -0,0 +1,301 @@ +/* + * 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 + * +*/ + +#include "mz.h" +#include "mops.h" +#include "cli.h" + +// Starts an ARP RX thread for *every* device in the device_list. +// (Except for the loopback interface) +// +// RETURN VALUE: 0 upon success, +// 1 upon error. +// +int mops_rx_arp () +{ + int i; + + for (i=0; i<device_list_entries; i++) { + if (mz_strcmp(device_list[i].dev, "lo", 2)==0) continue; // omit loopback! + if (pthread_create( &(device_list[i].arprx_thread), + NULL, + rx_arp, + &device_list[i])) { // give thread a pointer to that device_list entry + printf("xxxxxxxxxx\n"); + return 1; // Error creating thread + } else { + if (verbose) { + fprintf(stderr, " Started ARP monitor on device %s.\n", + device_list[i].dev); + } + } + } + return 0; +} + + +// Thread function to receive ARP responses for a given device. +// Runs forever - until Mausezahn stops (see clean_up()) +// +// Argument: pointer to device_struct! +// +// +// +void *rx_arp (void *arg) +{ + char errbuf[PCAP_ERRBUF_SIZE]; + struct pcap *p_arp; + struct bpf_program filter; + char filter_str[] = "arp"; // We want to analyze both requests and responses! + struct device_struct *dev = (struct device_struct*) arg; + + // FYI, possible filter string is also: + // "eth.dst==00:05:4e:51:01:b5 and arp and arp.opcode==2"; + + p_arp = pcap_open_live (dev->dev, + 100, // max num of bytes to read + 1, // 1 if promiscuous mode + PCAP_READ_TIMEOUT_MSEC, // read timeout 'until error' (-1 = indefinitely) + errbuf); + + if (p_arp == NULL) { + fprintf(stderr," rx_arp: [ERROR] %s\n",errbuf); + return NULL; // TODO: Should return pointer to error message or something similar + } + + dev->p_arp = p_arp; // also assign pointer to a global which is needed for clean_up + + if ( pcap_compile(p_arp, + &filter, // the compiled version of the filter + filter_str, // text version of filter + 0, // 1 = optimize + 0) // netmask + == -1) { + fprintf(stderr," rx_arp: [ERROR] Error calling pcap_compile\n"); + return NULL; + } + + if ( pcap_setfilter(p_arp, &filter) == -1) { + fprintf(stderr," rx_arp: [ERROR] Error setting pcap filter\n"); + pcap_perror(p_arp, " rx_arp: "); + return NULL; + } + + if (pcap_setdirection(p_arp, PCAP_D_IN) == -1) { + pcap_perror(p_arp, " rx_arp: "); + return NULL; + } + + again: + pcap_loop (p_arp, + 1, // number of packets to wait + got_arp_packet, // name of callback function + (u_char*) dev); // optional additional arguments for callback function + goto again; + + pthread_exit(NULL); // destroy thread + return NULL; +} + + +void got_arp_packet (u_char *args, + const struct pcap_pkthdr *header, // statistics about the packet (see 'struct pcap_pkthdr') + const u_char *packet) // the bytestring sniffed +{ + const struct struct_ethernet *ethernet; + const struct struct_arp *arp; + int size_ethernet = sizeof(struct struct_ethernet); + struct device_struct *dev = (struct device_struct*) args; + + u_int8_t + da[6], // eth da + sa[6], // eth sa + smac[6], // source hw address + sip[4], // source protocol address + tmac[6], // target hw address + tip[4]; // target protocol address + u_int16_t op; // operation + u_int32_t sec, nsec; + u_int8_t *x; + + // These are the most important lines here: + ethernet = (struct struct_ethernet*)(packet); + arp = (struct struct_arp*)(packet+size_ethernet); + sec = (u_int32_t) header->ts.tv_sec; + nsec = (u_int32_t) ((header->ts.tv_usec) * 1000); + + op = arp->arp_op; // note that we don't have network byte order anymore! + // tmact is: + // 100 instead of 00:01 (request) + // 200 instead of 00:02 (response) + + memcpy((void*) da, (void*) ethernet->eth_da, 6); + memcpy((void*) sa, (void*) ethernet->eth_sa, 6); + memcpy((void*) smac, (void*) arp->arp_smac, 6); + memcpy((void*) sip, (void*) arp->arp_sip, 4); + memcpy((void*) tmac, (void*) arp->arp_tmac, 6); + memcpy((void*) tip, (void*) arp->arp_tip, 4); + + // Only handle the packet if it is really an ARP response! + ////AND if it is not sent by THIS host! (not possible, we only scan inbound!) + x = (u_int8_t*) & op; + if (*(x+1) == 0x02) { + // ARP RESPONSE: Update ARP table + arptable_add(dev, sa, da, smac, sip, sec, nsec); + } else if (*(x+1) == 0x01) { + // ARP REQUEST: Detect poisoning attacks + arpwatch(dev, sa, da, smac, sip, tmac, tip, sec, nsec); + } + + + + + // ARP binding consists of: sip (IP) - smac (MAC) + // + // User alert, 2 possibilities: + // + // 1. Learned new binding: does smac belong to sip? + // + // 2. Alert: Mismatch of stored versus announced sip-to-smac binding + // + // In both cases user action: [Learn] [Ignore] [Attack] [Amok Attack] + // Countermeasures: Mausezahn him! + // + // ALSO correct ARP tables of other hosts, especially on the default gateway + // that is, send arp replies with true binding + // + // Finally: Create logging message + +} + + + +// Add new entry in device-specific ARP table +// but first check if already existing or change. +// +// RETURN VALUE: 0 upon success +// 1 upon error +// +int arptable_add(struct device_struct *dev, + u_int8_t *sa, + u_int8_t *da, + u_int8_t *smac, + u_int8_t *sip, + u_int32_t sec, + u_int32_t nsec) +{ + struct arp_table_struct *prev=NULL, *cur = dev->arp_table; + int i=0, alert=0; + + // If SA and SMAC are different this might be a MITM !!! + if (compare_mac(smac, sa)) alert=1; + + // Check if IP (sip) is already existing in arp table: + while (cur!=NULL) { + if (compare_ip(sip, cur->sip)==0) { // IP found! + timestamp_hms(cur->when); + if (da[0]==0xff) cur->bc_resp++; + else cur->uni_resp++; + if (compare_mac(smac, cur->smac)==0) { + // entry identical ! + cur->sec=sec; + cur->nsec=nsec; + return 0; + } else { + // entry with other MAC address found ! + if (cur->locked==0) { + cur->changed++; + memcpy((void*) cur->smac_prev, (void*) cur->smac, 6); + memcpy((void*) cur->smac, (void*) smac, 6); + cur->sec_prev=cur->sec; + cur->nsec_prev=cur->nsec; + cur->sec=sec; + cur->nsec=nsec; + if (alert) cur->flags|=0x02; + } + return 0; + } + } + prev = cur; + cur = cur->next; + i++; + } + + // If we get here, then there was no entry for that IP yet! + // Create new arp_table entry: + cur = (struct arp_table_struct *) malloc(sizeof(struct arp_table_struct)); + if (cur==NULL) return 1; + + // Append element: + if (dev->arp_table==NULL) dev->arp_table = cur; + else prev->next = cur; + + memcpy((void*) cur->sa, (void*) sa, 6); + memcpy((void*) cur->smac, (void*) smac, 6); + cur->smac_prev[0]=0x00; + cur->smac_prev[1]=0x00; + cur->smac_prev[2]=0x00; + cur->smac_prev[3]=0x00; + cur->smac_prev[4]=0x00; + cur->smac_prev[5]=0x00; + memcpy((void*) cur->sip, (void*) sip, 4); + if (da[0]==0xff) { + cur->bc_resp=1; + cur->uni_resp=0; + } else { + cur->bc_resp=0; + cur->uni_resp=1; + } + cur->changed=1; + cur->locked=0; + cur->dynamic=1; + cur->flags=0; + cur->sec=sec; + cur->nsec=nsec; + cur->sec_prev=0; + cur->nsec_prev=0; + cur->index=i+1; // I assume users prefer to count from 1. + timestamp_hms(cur->when); + if (alert) cur->flags|=0x02; + cur->next=NULL; + return 0; +} + + + +// Validate ARP requests +int arpwatch(struct device_struct *dev, + u_int8_t *sa, + u_int8_t *da, + u_int8_t *smac, + u_int8_t *sip, + u_int8_t *tmac, + u_int8_t *tip, + u_int32_t sec, + u_int32_t nsec) +{ + // Unicast requests are considered as anomaly + + if ((da[0]&0x01)==0) { // broadcast bit NOT set? + fprintf(stderr, "NOTE: Non-broadcast ARP request from %02x:%02x:%02x:%02x:%02x:%02x\n", + sa[0], sa[1], sa[2], sa[3], sa[4], sa[5]); + } + + return 0; +} + diff --git a/staging/mz.h b/staging/mz.h new file mode 100644 index 0000000..cad091f --- /dev/null +++ b/staging/mz.h @@ -0,0 +1,931 @@ +/* + * 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 + * +*/ + + + +#ifndef __MAUSEZAHN__ +#define __MAUSEZAHN__ + +#define _GNU_SOURCE +#include <libnet.h> +#include <pcap/pcap.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <limits.h> +#include <sys/time.h> +#include <time.h> +#include <signal.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <sys/ioctl.h> +#include <netinet/in.h> +#include <stdarg.h> +#include <math.h> + +extern int verbose_level; + +static inline void verbose_l1(const char *format, ...) +{ + va_list vl; + + if (verbose_level < 1) + return; + + va_start(vl, format); + vfprintf(stderr, format, vl); + va_end(vl); +} + +static inline void verbose_l2(const char *format, ...) +{ + va_list vl; + + if (verbose_level < 2) + return; + + va_start(vl, format); + vfprintf(stderr, format, vl); + va_end(vl); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// +#define MAUSEZAHN_VERSION "Mausezahn 0.40 - (C) 2007-2010 by Herbert Haas - http://www.perihel.at/sec/mz/" +#define MAUSEZAHN_VERSION_SHORT "0.40" +// +// +//////////////////////////////////////////////////////////////////////////////////////////////////////////// + + +// "Dies ist ein schrecklicher Ort." + +#define MZ_DEFAULT_CONFIG_PATH "/etc/mausezahn/" // see also mz_default_config_path below +#define MZ_DEFAULT_LOG_PATH "/var/log/mausezahn/" // see also mz_default_log_path below + +#define SLEEP usleep // The sleep function to use. Consider 'nanosleep' in future. +#define DEFAULT_DELAY 0 +#define PCAP_READ_TIMEOUT_MSEC 1 // The read timeout for pcap_open_live() +#define MZ_MAX_DEVICES 10 // Max number of network devices supported +#define MAX_PAYLOAD_SIZE 3*8192 +#define MAX_DNS_NAME 256 +#define MAX_8021Q_TAGS 16 +#define TIME_COUNT_MAX 10000 // the size of the timestamp arrays timeRX and timeTX upon creation +#define TIME_COUNT 100 // the default used-size of the timestamp arrays timeRX and timeTX +#define MAX_DATA_BLOCKS 1000 // how many data blocks of size TIME_COUNT-1 should be written per file +#define MAXBYTES_TO_READ 1500 // how many bytes the pcap routine should read from net +#define RCV_RTP_MAX_BAR_WIDTH 500 // max line-width printed in BAR mode (see rcv_rtp.c) + +#define ETH_SRC 1 // These are only some symbols used by some functions. (Don't touch) +#define ETH_DST 2 // These are only some symbols used by some functions. +#define SRC_PORT 1 // These are only some symbols used by some functions. +#define DST_PORT 2 // These are only some symbols used by some functions. + +#define TEST fprintf(stderr, "HERE at line %i in file %s\n", __LINE__,__FILE__ ); fflush(stderr); + + +// ----- PCAP-specific definitions: --------------------- +#define IPADDRSIZE 46 + + +int MZ_SIZE_LONG_INT; + +char mz_default_config_path[256]; +char mz_default_log_path[256]; + + +struct arp_table_struct { + int index; // an entry index (1, 2, ...) for easier user access + u_int8_t sa[6]; // sent by this MAC SA + u_int8_t smac[6]; // announced MAC + u_int8_t smac_prev[6]; // previously announced MAC + u_int8_t sip[4]; // announced IP + unsigned long int uni_rq; // count unidirectional ARP requests for this IP + unsigned long int bc_resp; // count broadcast ARP responses for this IP + unsigned long int uni_resp; // count normal (unidir) ARP responses for this IP + unsigned long int changed; // count how often the MAC address has changed! + int locked; // 1=this entry cannot be overidden anymore + int dynamic; // 1=learned dynamically, 0=configured by user + int flags; // anomaly information (length anomaly: bit 0, sa!=smac: bit 1 , ...) + int gw; // 1=Default GW + char when[10]; // human readable timestamp (e. g. "11:42:53") + u_int32_t sec, nsec; // timestamp of last ARP response + u_int32_t sec_prev, nsec_prev; // timestamp of previous ARP response + //-----------------// + struct arp_table_struct *next; +}; + +// Device list +struct device_struct +{ + char dev[16]; // Device name + int index; // Device index (assigned by OS) + int phy; // 1 if physical, 0 if not (e. g. loopback) + int mtu; + int cli; // if set to 1 then the CLI connection must terminate here + int mgmt_only; // if set to 1 then no data traffic is allowed through that interface + // ---- MAC addresses ---- + u_int8_t mac[6]; // Real MAC address + u_int8_t mac_mops[6]; // MAC address to be used + // ---- IP related ----- + char ip_str[IPADDRSIZE+1]; // Real IP address as string in dotted decimal notation + u_int8_t ip[4]; // Real IP address + u_int8_t net[4]; // Real network + u_int8_t mask[4]; // Real mask + u_int8_t ip_mops[4]; // IP address to be used + // ---- Default Gateway per interface: + u_int8_t mac_gw[6]; // MAC address of default gateway + u_int8_t ip_gw[4]; // IP address of default gateway + // ---- various device-specific handles ---- + pthread_t arprx_thread; + struct pcap *p_arp; // pcap handle + struct arp_table_struct *arp_table; // dedicated ARP table + int ps; // packet socket +} device_list[MZ_MAX_DEVICES]; + +int device_list_entries; + + +#pragma pack(1) +struct struct_ethernet +{ + u_int8_t eth_da[6]; + u_int8_t eth_sa[6]; + u_int16_t eth_type; +}; + +struct struct_arp +{ + u_int16_t arp_hrd; // hardware address format + u_int16_t arp_pro; // protocol address format + u_int8_t arp_hln; // hardware address length + u_int8_t arp_pln; // protocol address length + u_int16_t arp_op; // ARP operation type + u_int8_t arp_smac[6]; // sender's hardware address + u_int8_t arp_sip[4]; // sender's protocol address + u_int8_t arp_tmac[6]; // target hardware address + u_int8_t arp_tip[4]; // target protocol address +}; + + + +//#pragma pack(1) +struct struct_ip +{ + u_int8_t + hlen :4, + ver :4; + u_int8_t + tos; + u_int16_t + len; + + u_int16_t + id, + offset; // flags and fragment offset field + + u_int8_t + ttl, + proto; + u_int16_t + sum; + + u_int8_t src[4]; + u_int8_t dst[4]; +}; + +//#pragma pack(1) +struct struct_udp { + u_int16_t + sp, + dp, + len, + sum; +}; + +//#pragma pack(1) +struct struct_rtp { + u_int8_t + byte1, + ptype; + u_int16_t + sqnr; + u_int32_t + timestamp, // official timestamp, created by codecs + ssrc; + // csrc, // only used by mixers + u_int16_t + ext_id, + ext_len; + u_int32_t + time_sec, + time_nsec, + time_sec2, + time_nsec2; +}; + +// ---------End of PCAP-specific definitions--------------- + + + + +// ************************************ +// +// Global variables +// +// ************************************ + +enum operating_modes +{ + BYTE_STREAM, + ARP, + BPDU, + IP, + ICMP, + ICMP6, + UDP, + TCP, + DNS, + CDP, + RTP, + RX_RTP, + SYSLOG, + LLDP +} mode; + + +int ipv6_mode; +int quiet; // don't even print 'important standard short messages' +int verbose; // report character +int simulate; // if 1 then don't really send frames + +char path[256]; +char filename[256]; +FILE *fp, *fp2; // global multipurpose file pointer + +long double total_d; +clock_t mz_start, mz_stop; + +enum rtp_display_mode { + BAR, NCURSES, TEXT +} rtp_dm; + + +int mz_rand; +int bwidth; + +struct mz_timestamp { + u_int32_t sec; + u_int32_t nsec; +}; + +struct mz_timestamp + tv, + timeTX[TIME_COUNT_MAX], + timeRX[TIME_COUNT_MAX]; + +int32_t + time0, + jitter_rfc, + jitter[TIME_COUNT_MAX]; + +int + rtp_log, + time0_flag, // If set then time0 has valid data + sqnr0_flag; + +u_int8_t + mz_ssrc[4]; // holds RTP stream identifier for rcv_rtp() + +u_int16_t + sqnr_cur, + sqnr_last, + sqnr_next; + +u_int32_t + drop, // packet drop count + dis, // packet disorder count + gind, // a global index to run through deltaRX, deltaTX, and jitter + gind_max, // the amount of entries used in the (ugly oversized) arrays; per default set to TIME_COUNT + gtotal; // counts number of file write cycles (see "got_rtp_packet()") + + +char rtp_filter_str[64]; + +struct tx_struct +{ + // Management issues for TX + char device[16]; // every packet could be sent through a different device + int packet_mode; // 0 means use LIBNET_LINK_ADV, 1 means LIBNET_RAW4 + unsigned int count; // 0 means infinite, 1 is default + unsigned int delay; // Delay in microseconds, 0 means no delay (default) + char arg_string[MAX_PAYLOAD_SIZE]; // Argument-string when -t is used + + // Ethernet and 802.3 parameters + int eth_params_already_set; // if set to 1 then send_eth should only send the frame + u_int8_t eth_mac_own[6]; // Contains own interface MAC if needed by some modules + char eth_dst_txt[32]; // Text version of eth_dst (or keyword such as 'rand') + u_int8_t eth_dst[6]; + int eth_dst_rand; // 1 if random + char eth_src_txt[32]; // Text version of eth_src (or keyword such as 'rand') + u_int8_t eth_src[6]; + int eth_src_rand; // 1 if random + u_int16_t eth_type; + u_int16_t eth_len; + u_int8_t eth_payload[MAX_PAYLOAD_SIZE]; + u_int32_t eth_payload_s; + unsigned int padding; + + // CDP parameters + u_int8_t + cdp_version, + cdp_ttl, + cdp_payload[MAX_PAYLOAD_SIZE], + cdp_tlv_id[2048]; // The ID is the only required TLV + u_int16_t + cdp_sum; + u_int32_t + cdp_tlv_id_len, + cdp_payload_s; + + // 802.1Q VLAN Tag + int dot1Q; // 1 if specified + char dot1Q_txt[32]; // contains 802.1p(CoS) and VLAN-ID ("5:130" or only VLAN "130") + u_int8_t dot1Q_CoS; + u_int16_t dot1Q_vlan; + u_int8_t dot1Q_header[256]; // Contains the complete 802.1Q/P headers (but NOT the Ethernet header!) + u_int8_t dot1Q_header_s; + int dot1Q_at_least_two_headers; // If '1' then we have at least QinQ (or more VLAN tags) + + // ASCII PAYLOAD + int ascii; // 1 if specified + u_int8_t ascii_payload[MAX_PAYLOAD_SIZE]; + + // HEX PAYLOAD + u_int8_t hex_payload[MAX_PAYLOAD_SIZE]; + u_int32_t hex_payload_s; // >0 if hex payload is specified + + // MPLS Parameters + char mpls_txt[128]; // contains MPLS parameters (label, exp, S, TTL) + char mpls_verbose_string[1024]; // contains all labels for print_frame_details() + int mpls; // 1 if specified + u_int32_t mpls_label; + u_int8_t mpls_exp; + u_int8_t mpls_bos; + u_int8_t mpls_ttl; + + // IP parameters + u_int32_t ip_src; // has always network byte order(!) + struct libnet_in6_addr ip6_src; + char ip_src_txt[256]; + int ip_src_rand; // if set to 1 then SA should be random + u_int32_t ip_src_h; // mirror of ip_src (NOT network byte order => easy to count) + u_int32_t ip_src_start; // start of range (NOT network byte order => easy to count) + u_int32_t ip_src_stop; // stop of range (NOT network byte order => easy to count) + int ip_src_isrange; // if set to 1 then the start/stop values above are valid. + u_int32_t ip_dst; // has always network byte order(!) + struct libnet_in6_addr ip6_dst; + char ip_dst_txt[256]; + u_int32_t ip_dst_h; // mirror of ip_dst (NOT network byte order => easy to count) + u_int32_t ip_dst_start; // start of range (NOT network byte order => easy to count) + u_int32_t ip_dst_stop; // stop of range (NOT network byte order => easy to count) + int ip_dst_isrange; // if set to 1 then the start/stop values above are valid. + u_int16_t + ip_len, + ip_id, + ip_frag, // Flags and Offset !!! + ip_sum; + u_int8_t + ip_tos, + ip_ttl, + ip6_rtype, + ip6_segs, + ip_proto; + u_int8_t + ip_option[1024], + ip_payload[MAX_PAYLOAD_SIZE]; + u_int32_t + ip_flow, + ip6_id, + ip_option_s, + ip_payload_s; + + // ICMP + char + icmp_verbose_txt[256]; // used for verbose messages in send.c + u_int8_t + icmp_type, + icmp_code; + u_int16_t icmp_ident; // ATTENTION: libnet.h already #defines 'icmp_id', 'icmp_sum', and 'icmp_num' + u_int16_t icmp_chksum; // therefore I needed a renaming here -- be careful in future... + u_int16_t icmp_sqnr; // + u_int32_t + icmp_gateway, + icmp_payload_s; + u_int8_t + icmp_payload[MAX_PAYLOAD_SIZE]; + + // General L4 parameters: + char *layer4; + u_int16_t + sp, dp, + sp_start, sp_stop, + dp_start, dp_stop; + int + sp_isrange, // if set to 1 then start/stop values above are valid + dp_isrange; // if set to 1 then start/stop values above are valid + + // UDP parameters + u_int16_t + udp_len, // includes header size (8 bytes) + udp_sum; + u_int8_t + udp_payload[MAX_PAYLOAD_SIZE]; + u_int32_t + udp_payload_s; + + // TCP parameters + u_int32_t + tcp_seq, + tcp_seq_start, + tcp_seq_stop, // is always set! Usually seq_start = seq_stop (=no range) + tcp_seq_delta, // Also used instead of an 'isrange' variable + tcp_ack; + u_int8_t + tcp_offset, + tcp_control; + u_int16_t + tcp_win, + tcp_sum, + tcp_urg, + tcp_len; // only needed by libnet and must include header size + u_int8_t + tcp_payload[MAX_PAYLOAD_SIZE]; + u_int32_t + tcp_sum_part, + tcp_payload_s; + + // RTP parameters + u_int32_t + rtp_sqnr, + rtp_stmp; + +} tx; // NOTE: tx elements are considered as default values for MOPS + + + + + +u_int8_t gbuf[MAX_PAYLOAD_SIZE]; // This is only a generic global buffer to handover data more easily +u_int32_t gbuf_s; // + + +// ************************************ +// +// Prototypes: General Tools +// +// ************************************ + +void clean_up(int sig); +int getopts(int argc, char *argv[]); +int getarg(char *str, char *arg_name, char *arg_value); +unsigned long int str2int(char *str); // converts "65535" to 65535 +unsigned long long int str2lint(char *str); // same but allows 64-bit integers +unsigned long int xstr2int(char *str); // converts "ffff" to 65535 +unsigned long long int xstr2lint(char *str); // same but allows 64-bit integers +int mz_strisbinary(char *str); +int mz_strisnum(char *str); +int mz_strishex(char *str); +int str2bin8 (char *str); +long int str2bin16 (char *str); +int char2bits (char c, char *str); +int mz_strcmp(char* usr, char* str, int min); +int mz_tok(char * str, char * delim, int anz, ...); +int delay_parse (struct timespec *t, char *a, char *b); +int reset(); + +// ************************************ +// +// Prototypes: Layer1 +// +// ************************************ + +int send_eth(void); +libnet_ptag_t create_eth_frame (libnet_t *l, libnet_ptag_t t3, libnet_ptag_t t4); + +// ************************************ +// +// Prototypes: Layer 2 +// +// ************************************ + +int send_arp (void); +int send_bpdu (void); +int send_cdp (void); + +// ************************************ +// +// Prototypes: Layer 3 +// +// ************************************ + + +libnet_t* get_link_context(void); +libnet_ptag_t create_ip_packet (libnet_t *l); +libnet_ptag_t create_ip6_packet (libnet_t *l); +int send_frame (libnet_t *l, libnet_ptag_t t3, libnet_ptag_t t4); + + + +// ************************************ +// +// Prototypes: Layer 4 +// +// ************************************ +libnet_ptag_t create_udp_packet (libnet_t *l); +libnet_ptag_t create_icmp_packet (libnet_t *l); +libnet_ptag_t create_icmp6_packet (libnet_t *l); +libnet_ptag_t create_tcp_packet (libnet_t *l); + + +// ************************************ +// +// Prototypes: Layer 7 +// +// ************************************ +int create_dns_packet (void); +int create_rtp_packet(void); +int create_syslog_packet(void); + +// ************************************ +// +// Prototypes: Helper functions for +// byte manipulation, +// address conversion, +// etc +// +// ************************************ + +// Converts MAC address specified in str into u_int8_t array +// Usage: str2hex_mac ( "00:01:02:aa:ff:ee", src_addr ) +int str2hex_mac (char* str, u_int8_t *addr); + +// Converts ascii hex values (string) into integer array, similarly as above but for any size. +// Example: "1a 00:00-2f" => {26, 0, 0, 47} +// Note: apply any improvements here and prefer this function in future! +// Return value: Number of converted elements (=length of array) +int str2hex (char* str, u_int8_t *hp, int n); + +// Converts ascii numbers (string) into integer array +// Every byte can be specified as integers {0..255} +// For example "192.16.1.1" will be converted to {C0, 10, 01, 01} +int num2hex(char* str, u_int8_t *hp); + +// Convert array of integers into string of hex. Useful for verification messages. +// Example: {0,1,10} => "00-01-0A" +// Usage: bs2str ( src_mac, src_mac_txt, 6 ) +int bs2str (u_int8_t *bs, char* str, int len); + +// Extract contiguous sequence of bytes from an array. First element has index 1 !!! +// Usage: getbytes (bs, da, 1, 6); +int getbytes(u_int8_t *source, u_int8_t *target, int from, int to); + +// For any IP address given in 'dotted decimal' returns an unsigned 32-bit integer. +// Example: "192.168.0.1" => 3232235521 +// Note: Result is in LITTLE ENDIAN but usually with IP you need BIG ENDIAN, see next. +u_int32_t str2ip32 (char* str); + +// For any IP address given in 'dotted decimal' into an unsigned 32-bit integer +// This version does the same as str2ip32() but in BIG ENDIAN. +// Note: With netlib you need this one, not the previous function. +u_int32_t str2ip32_rev (char* str); + +// Converts a 2-byte value (e. g. a EtherType field) +// into a nice string using hex notation. +// Useful for verification messages. +// Example: type2str (tx.eth_type, msg) may result in msg="08:00" +// Return value: how many hex digits have been found. +int type2str(u_int16_t type, char *str); + + +// Parses string 'arg' for an IP range and finds start and stop IP addresses. +// Return value: 0 upon success, 1 upon failure. +// +// NOTE: The results are written in the following variables: +// +// (u_int32_t) tx.ip_dst_start ... contains start value +// (u_int32_t) tx.ip_dst_stop ... contains stop value +// int tx.ip_dst_isrange ... set to 1 if above values valid +// +// The other function does the same for the source address! +// +// Possible range specifications: +// +// 1) 192.168.0.0-192.168.0.12 +// 2) 10.2.11.0-10.55.13.2 +// 3) 172.18.96.0/19 +// +// That is: +// +// FIRST detect a range by scanning for the "-" OR "/" chars +// THEN determine start and stop value and store them as normal unsigned integers +// +int get_ip_range_dst (char *arg); +int get_ip_range_src (char *arg); + +// Sets a random SA for a given IP packet. +// Return value: 0 upon success, 1 upon failure +// +int set_rand_SA (libnet_t *l, libnet_ptag_t t3); + +// Scans tx.eth_dst_txt or tx.eth_src_txt and sets the corresponding +// MAC addresses (tx.eth_dst or tx.eth_src) accordingly. +// Argument: What string should be checked, ETH_SRC or ETH_DST. +// Return value: +// 0 when a MAC address has been set or +// 1 upon failure. +// Currently eth_src|dst_txt can be: +// 'rand', 'own', 'bc'|'bcast', 'stp', 'pvst', +// or a real mac address. +// +int check_eth_mac_txt(int src_or_dst); + +// Scans argument for a port number or range +// and sets the corresponding values in the +// tx struct. +// +// Arguments: sp_or_dp is either SRC_PORT or DST_PORT +// Return value: 0 on success, 1 upon failure +// +int get_port_range (int sp_or_dp, char *arg); + +// Return a 4-byte unsigned int random number +u_int32_t mz_rand32 (void); + +// Scans argument for TCP flags and sets +// tx.tcp_control accordingly. +// +// Valid keywords are: fin, syn, rst, psh, ack, urg, ecn, cwr +// Valid delimiters are: | or + or - +// Return value: 0 on success, 1 upon failure +// +int get_tcp_flags (char* flags); + +// Scans string 'params' for MPLS parameters +// and sets tx.mpls_* accordingly. +// +// CLI Syntax Examples: +// +// -M help .... shows syntax +// +// -M 800 .... label=800 +// -M 800:S .... label=800 and BOS flag set +// -M 800:S:64 .... label=800, BOS, TTL=64 +// -M 800:64:S .... same +// -M 64:77 .... label=64, TTL=77 +// -M 64:800 .... INVALID +// -M 800:64 .... label=800, TTL=64 +// -M 800:3:S:64 .... additionall the experimental bits are set (all fields required!) +// +// Note: S = BOS(1), s = NOT-BOS(0) +// +// Valid delimiters: :-.,+ +// Return value: 0 on success, 1 upon failure +int get_mpls_params(char *params); + +// Parses str for occurence of character or sequence ch. +// Returns number of occurences +int exists(char* str, char* ch); + + +// Applies another random Ethernet source address to a given Ethernet-PTAG. +// (The calling function should check 'tx.eth_src_rand' whether the SA +// should be randomized.) +int update_Eth_SA(libnet_t *l, libnet_ptag_t t); + + +// Update timestamp and sequence number in the RTP header. +// The actual RTP message is stored in tx.udp_payload. +int update_RTP(libnet_t *l, libnet_ptag_t t); + + +// Applies another SOURCE IP address, +// - either a random one (tx.ip_src_rand==1) +// - or from a specified range (tx.ip_src_isrange==1) +// to a given IP-PTAG. +// +// Note: tx.ip_src MUST be already initialized with tx.ip_src_start. +// This is done by 'get_ip_range_src()' in tools.c. +// +// RETURNS '1' if tx.ip_src restarts +int update_IP_SA (libnet_t *l, libnet_ptag_t t); + + +// Applies another DESTINATION IP address from a specified range (tx.ip_dst_isrange==1) +// to a given IP-PTAG. +// +// Note: tx.ip_dst MUST be already initialized with tx.ip_dst_start. +// This is done by 'get_ip_range_dst()' in tools.c. +// +// RETURN VALUE: '1' if tx.ip_dst restarts +int update_IP_DA(libnet_t *l, libnet_ptag_t t); + + +// Applies another DESTINATION PORT from a specified range to a given UDP- or TCP-PTAG. +// +// Note: tx.dp MUST be already initialized with tx.dp_start +// This is done by 'get_port_range()' in tools.c. +// +// RETURN VALUE: '1' if tx.dp restarts +int update_DPORT(libnet_t *l, libnet_ptag_t t); + + +// Applies another SOURCE PORT from a specified range to a given UDP- or TCP-PTAG. +// +// Note: tx.sp MUST be already initialized with tx.sp_start +// This is done by 'get_port_range()' in tools.c. +// +// RETURN VALUE: '1' if tx.sp restarts +int update_SPORT(libnet_t *l, libnet_ptag_t t); + + +// Applies another TCP SQNR from a specified range to a given TCP-PTAG +// +// RETURN VALUE: '1' if tx.txp_seq restarts +// +int update_TCP_SQNR(libnet_t *l, libnet_ptag_t t); + +int update_ISUM(libnet_t *l, libnet_ptag_t t); +int update_USUM(libnet_t *l, libnet_ptag_t t); +int update_TSUM(libnet_t *l, libnet_ptag_t t); + +// +// +int print_frame_details(void); + + +// Calculates the number of frames to be sent. +// Should be used as standard output except the +// 'quiet' option (-q) has been specified. +int complexity(void); + + +// Purpose: Calculate time deltas of two timestamps stored in struct timeval. +// Subtract the "struct timeval" values X and Y, storing the result in RESULT. +// Return 1 if the difference is negative, otherwise 0. +int timestamp_subtract (struct mz_timestamp *x, + struct mz_timestamp *y, + struct mz_timestamp *result); + +void timestamp_add (struct mz_timestamp *x, + struct mz_timestamp *y, + struct mz_timestamp *result); + +// Returns a human readable timestamp in the string result. +// Optionally a prefix can be specified, for example if the +// timestamp is part of a filename. +// +// Example: +// char myTimeStamp[128]; +// +// timestamp_human(myTimeStamp, NULL); +// +// => "20080718_155521" +// +// /* or with prefix */ +// +// timestamp_human(myTimeStamp, "MZ_RTP_jitter_"); +// +// => MZ_RTP_jitter_20080718_155521 +// +int timestamp_human(char* result, const char* prefix); + +// Returns a human readable timestamp in the string result. +// Optionally a prefix can be specified, for example if the +// timestamp is part of a filename. +// +// Example: +// char myTimeStamp[8]; +// +// timestamp_hms (myTimeStamp); +// +// => "15:55:21" +int timestamp_hms(char* result); + +// Initialize the rcv_rtp process: Read user parameters and initialize globals +int rcv_rtp_init(void); + +// Defines the pcap handler and the callback function +int rcv_rtp(void); + +// Print current RFC-Jitter on screen +void print_jitterbar (long int j, unsigned int d); + +// Compares two 4-byte variables byte by byte +// returns 0 if identical, 1 if different +int compare4B (u_int8_t *ip1, u_int8_t *ip2); + +// PURPOSE: Find usable network devices +// +// NOTE: +// +// 1. Ignores devices without IP address +// 2. Ignores loopback (etc) +// +// RETURN VALUES: +// +// 0 if usable device found (device_list[] and tx.device set) +// 1 if no usable device found +// +int lookupdev(void); + + +// For a given device name, find out the following parameters: +// +// - MTU +// - Network +// - Mask +// - Default GW (IP) +// +int get_dev_params (char *name); + +// Handler function to do something when RTP messages are received +void got_rtp_packet(u_char *args, + const struct pcap_pkthdr *header, // statistics about the packet (see 'struct pcap_pkthdr') + const u_char *packet); // the bytestring sniffed + + +// Check if current system supports the nanosecond timer functions. +// Additionally, measure the precision. +// This function should be called upon program start. +// +int check_timer(void); + +// This is the replacement for gettimeofday() which would result in 'jumps' if +// the system clock is adjusted (e. g. via a NTP process) and finally the jitter +// measurement would include wrong datapoints. +// +// Furthermore the function below utilizes the newer hi-res nanosecond timers. +void getcurtime (struct mz_timestamp *t); + +// Only print out the help text for the 02.1Q option +void print_dot1Q_help(void); + +// Determines ip and mac address of specified interface 'ifname' +// Caller must provide an unsigned char ip[4], mac[6] +// +int get_if_addr (char *ifname, unsigned char *ip, unsigned char *mac); + +// Takes filename and prepends valid configuration/logging directory +// NOTE: filename is overwritten and must be big enough to hold full path! +int getfullpath_cfg (char *filename); +int getfullpath_log (char *filename); + +// A safer replacement for strncpy which ensures \0-termination +char * mz_strncpy(char *dest, const char *src, size_t n); + +// Helper function to count the number of arguments +// in the Mausezahn argument string (comma separated args) +// RETURN VALUE: Number of arguments +int number_of_args (char *str); + +int arptable_add(struct device_struct *dev, + u_int8_t *sa, + u_int8_t *da, + u_int8_t *smac, + u_int8_t *sip, + u_int32_t sec, + u_int32_t nsec); + +// Validate ARP requests +int arpwatch(struct device_struct *dev, + u_int8_t *sa, + u_int8_t *da, + u_int8_t *smac, + u_int8_t *sip, + u_int8_t *tmac, + u_int8_t *tip, + u_int32_t sec, + u_int32_t nsec); + + +#endif diff --git a/staging/parse_xml.c b/staging/parse_xml.c new file mode 100644 index 0000000..2189b83 --- /dev/null +++ b/staging/parse_xml.c @@ -0,0 +1,568 @@ +/* + * 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 + * +*/ + + +#include "mz.h" +#include "mops.h" +#include "cli.h" + + + +// Returns integer number for given tag string +// For example xml_tag2int("field") => xml_field == 1 +// +// Returns -1 when tag not known +int xml_tag2int (char *t) +{ + if (!strncasecmp(t, "protocol", XML_MAX_TAG_LEN)) + return xml_protocol; + + if (!strncasecmp(t, "field", XML_MAX_TAG_LEN)) + return xml_field; + + if (!strncasecmp(t, "name", XML_MAX_TAG_LEN)) + return xml_name; + + if (!strncasecmp(t, "desc", XML_MAX_TAG_LEN)) + return xml_desc; + + if (!strncasecmp(t, "requires", XML_MAX_TAG_LEN)) + return xml_requires; + + if (!strncasecmp(t, "conflicts", XML_MAX_TAG_LEN)) + return xml_conflicts; + + if (!strncasecmp(t, "payloadtype", XML_MAX_TAG_LEN)) + return xml_payloadtype; + + if (!strncasecmp(t, "payload", XML_MAX_TAG_LEN)) + return xml_payload; + + if (!strncasecmp(t, "payloadhex", XML_MAX_TAG_LEN)) + return xml_payloadhex; + + if (!strncasecmp(t, "index", XML_MAX_TAG_LEN)) + return xml_index; + + if (!strncasecmp(t, "longdesc", XML_MAX_TAG_LEN)) + return xml_longdesc; + + if (!strncasecmp(t, "type", XML_MAX_TAG_LEN)) + return xml_type; + + if (!strncasecmp(t, "constant", XML_MAX_TAG_LEN)) + return xml_constant; + + if (!strncasecmp(t, "value", XML_MAX_TAG_LEN)) + return xml_value; + + if (!strncasecmp(t, "valname", XML_MAX_TAG_LEN)) + return xml_valname; + + if (!strncasecmp(t, "min", XML_MAX_TAG_LEN)) + return xml_min; + + if (!strncasecmp(t, "max", XML_MAX_TAG_LEN)) + return xml_max; + + if (!strncasecmp(t, "tlvt", XML_MAX_TAG_LEN)) + return xml_tlvt; + + if (!strncasecmp(t, "tlvl", XML_MAX_TAG_LEN)) + return xml_tlvl; + + if (!strncasecmp(t, "lshift", XML_MAX_TAG_LEN)) + return xml_lshift; + + return -1; +} + + +// For a given pair of tag t and parent p check +// if t is really an allowed child of p. +// RETURN VALUE: 0 if correct, -1 otherwise +// +int xml_check_parent(int t, int p) +{ + // For given tag t specify allowed parent p + switch (t) { + + // no parent allowed + case xml_protocol: + if (p==-1) return 0; + break; + + // has protocol as parent + case xml_field: + case xml_requires: + case xml_conflicts: + case xml_payloadtype: + case xml_payload: + case xml_payloadhex: + if (p==xml_protocol) return 0; + break; + + // has field OR protocol as parent + case xml_name: + case xml_desc: + if ((p==xml_protocol)||(p==xml_field)) return 0; + break; + + // has field as parent + case xml_longdesc: + case xml_type: + case xml_constant: + case xml_valname: + case xml_value: + case xml_min: + case xml_max: + case xml_index: + case xml_lshift: + case xml_tlvt: + case xml_tlvl: + if (p==xml_field) return 0; + + } + return -1; +} + + +// Parse a single protocol definition. +// The string 'p' must start with '<protocol>' and end with </protocol> +// +// RETURN VALUE: 0 upon success, >0 otherwise. +// +int parse_protocol (char *p) +{ + int i; + char p_clone[AUTOMOPS_MAX_FILE_SIZE+1]; + struct automops *new_amp; + + // Make a local copy of the protocol definition + strncpy(p_clone, p, AUTOMOPS_MAX_FILE_SIZE); + p_clone[AUTOMOPS_MAX_FILE_SIZE]='\0'; + + // Check if XML form is correct. + // I thought that this check should be done separately (and not during + // the xml_readin() function) for safety reasons. If read-in plus + // validation would be combined, we would have more to clean-up at in + // case the XML data is corrupt. + i = xml_canonic (p_clone); + + // If incorrect, tell where error is: + if ((!quiet) && (i)) { + p_clone[i+1]='\0'; + fprintf(stderr, "(EE) Mausezahn automops xml parse error:\n" + "========================================\n" + "%s <<ERROR>>\n", p_clone); + fprintf(stderr, "(EE) Error occured at character number %i\n", i); + fprintf(stderr," --- (printed all valid data until error position) ---\n"); + } + + if (verbose) { + fprintf(stderr, "...XML verification finished.\n"); + } + + // XML is correct, now create automops entry + + if (i==0) { + strncpy(p_clone, p, AUTOMOPS_MAX_FILE_SIZE); + p_clone[AUTOMOPS_MAX_FILE_SIZE]='\0'; + new_amp = automops_alloc_protocol(amp_head); + i = xml_readin(new_amp, p_clone); + + if ((!quiet) && (i)) { + if (verbose) { + p_clone[i+1]='\0'; + fprintf(stderr, "(EE) Invalid XML data at position %i: %s <<ERROR>>\n", + i, p_clone); + fprintf(stderr," --- (printed all valid data until error position) ---\n"); + } + automops_delete_protocol(new_amp); + } + } + return i; +} + + + +// Scans p until the next tag is found and stores +// tag name in t which must be a string of size +// XML_STRLEN (at least). +// +// Returns +// >0 if opening tag is found +// 0 if no tag is found or other problem +// <0 if closing tag is found +// +// If any tag is found the absolut return value +// indicates the position AFTER the tag, e. g. +// ...<tag>... or ...</tag>... +// ^here ^here +// +// Upon problem, the errorness char number is +// stored as string within t along with the +// error reason as string. +// +int xml_getnext_tag (char *p, char *t) +{ + int i=0,j=0,k=0, + sign=1, + len; + + // are there non-white characters left? + len = strnlen(p, AUTOMOPS_MAX_FILE_SIZE); + for (i=0; i<len; i++) if (!isspace(p[i])) j=1; // flag for first non-space character found + if (!j) { // no more characters found + t[0]=0x00; + return 0; + } + + // basic length checks + i=0; j=0; + if ((len<3)||(len==AUTOMOPS_MAX_FILE_SIZE)) { // 3 is minimum tag length + snprintf(t, XML_STRLEN, "invalid length (%u)",len); + return 0; + } + + + // find first opening ('<') bracket + do { + if (p[i]=='<') break; + i++; + } while (i<len); + + // tag too close to end + if (i>(len-3)) { + snprintf(t, XML_STRLEN, "%4i - no end", i); + return 0; // no tag found (smallest tag is '<x>') + } + + j=++i; + + // closing tag? + if (p[i]=='/') { + i++; + j++; + sign=-1; + } + + // find closing bracket + // and get tag name + do { + if (p[i]=='>') { + k=i; // =found + break; + } + i++; + if (i==len) { + snprintf(t, XML_STRLEN, "%4i - no end?", i); + return 0; + } + } while (i<(j+XML_MAX_TAG_LEN+1)); + + // closing '>' really found? + if (!k) { + sprintf(t, "%4i - closing bracket missing", i); + return 0; + } + + // now the tag name is from p[j]..p[k-1] + + memcpy((void*) t, (void*) &p[j], k-j); + t[k-j]='\0'; + + return sign*(k+1); +} + + +// Copies data between opening and closing XML tags +// into 't' and returns the length of the data in bytes +// or zero if nothing found +// or -1 if protocol or data length is too long +// Note: Assumes that *p points to first byte after opening tag! +int xml_get_data (char *p, char *t) +{ + int i=0, len; + + // basic length checks + len = strnlen(p, AUTOMOPS_MAX_FILE_SIZE); + if (len==0) return 0; + + if (len>AUTOMOPS_MAX_FILE_SIZE) { + snprintf(t, XML_STRLEN, "invalid length (%u)",len); + return -1; + } + + // find closing tag + // i. e. next opening ('<') bracket + do { + if (p[i]=='<') break; + i++; + } while (i<len); + + // Set limit on data length + if (i>1500) return -1; // TODO: consider more reasonable limit + + // copy data + memcpy((void*) t, (void*) &p[0], i); + t[i]='\0'; + return i; +} + + + +// Make some simple checks whether XML data correct +// Currently only checks if +// - every opening tag has an ending tag (only via verbose now) +// - tags are properly nested (only 1st order tests now) +// +// RETURN VALUE: 0 upon success +// or position of mistake +// +int xml_canonic (char *p) +{ + int i=0, l, dlen=0, plen, xntag=-1; + char t[XML_STRLEN]; + char d[1500]; + + struct xnstack stack, *s; + + s=&stack; + xnstack_init(s); + + if (verbose==2) { + fprintf(stderr, "Parsing {%s}\n\n", p); + } + + plen = strnlen(p, AUTOMOPS_MAX_FILE_SIZE); + + do { + l = xml_getnext_tag (p, t); // Now t contains next tag name and l tells whether open or closing + if (l==0) { + if (t[0]==0x00) // no more tag found + return 0; + else { // general failure + fprintf(stderr, "%s\n", t); + return i; + } + + } + i += abs(l); + if (verbose==2) { + fprintf(stderr, "%4i %4i stack=%i %s%s>\n",i,l,xnstack_size(s), + (l>0) ? "<" : "</", t); + } + if (i>=plen) { // break condition (regular, not an error!) + i=plen-1; + } + p+=abs(l); // now p points to first byte after tag + + if (xml_tag2int(t)<0) { + fprintf(stderr, "mz/xml_canonic: UNKNOWN TAG at position %i\n", i); + return i; + } + + // Closing tag found: does it match last opening tag? + if (l<0) { + if (xml_tag2int(t)!=xnstack_pop(s)) { + if (verbose) { + fprintf(stderr, "mz/xml_canonic: Incoherent nesting at position %i\n", i); + } + return i; + } + } + + // Opening tag found: store it in last_tag! + if (l>0) { + xntag=xml_tag2int(t); + // Check if this tag has proper parent + if (xml_check_parent(xntag, xnstack_get_top(s))) { + fprintf(stderr, "mz/xml_canonic: Wrong parent tag\n"); + return i; + } + if (xnstack_push(s, xntag)==-1) { + if (verbose) { + fprintf(stderr, "mz/xml_canonic: max nesting depth exceeded\n"); + } + return i; + } + // also print data: + dlen = xml_get_data (p, d); + if (dlen==-1) { + if (verbose) { + fprintf(stderr, "mz/xml_canonic: %s\n", d); + } + return i; + } + if ((dlen>0) && (verbose==2)) { + fprintf(stderr, " %s\n", d); // the data + } + + } + + if (i==plen-1) return 0; + } while (l!=0); + + if (xnstack_size(s)!=0) { + fprintf(stderr,"mz/xml_canonic: number of opening and closing tags does not match!\n"); + return i; + } + + return 0; +} + + + +// Copy data elements of *p into struct *amp +// ============================================================= +// NOTE: THE XML STRUCTURE MUST BE CORRECT !!! +// NO XML CHECKS ARE DONE TO KEEP THIS FUNCTION SMALL !!! +// THEREFORE ALWAYS RUN xml_canonic() FIRST !!! +// ============================================================= +// +// However, this function checks if the *data* is valid. +// +// RETURN VALUE: 0 upon success, +// otherwise character position of wrong data +// +int xml_readin (struct automops *amp, char *p) +{ + int i=0, l, dlen=0, plen, xntag=-1, parent=-1, err=0; + char t[XML_STRLEN]; + char d[1500], errmsg[64]; + + struct xnstack stack, *s; + struct fields *f=NULL; + + s=&stack; + xnstack_init(s); + + plen = strnlen(p, AUTOMOPS_MAX_FILE_SIZE); + + do { + l = xml_getnext_tag (p, t); // Now t contains next tag name and l tells whether open or closing + if (l==0) { + if (t[0]==0x00) return 0; + else + return i; + } + i += abs(l); + if (i>=plen) { // break condition (regular, not an error!) + i=plen-1; + } + p+=abs(l); // now p points to first byte after tag + + + // Closing tag found: does it match last opening tag? + if (l<0) xnstack_pop(s); + + // Opening tag found: store it in last_tag! + if (l>0) { + xntag=xml_tag2int(t); + parent=xnstack_get_top(s); // get parent tag; + xnstack_push(s, xntag); + dlen = xml_get_data (p, d); + + if (xntag==xml_field) { // Create new field + f=automops_add_field(amp); + } else + // Now copy the data 'd' into (the header & fields of) 'amp' + if (dlen>0) { + if (parent==xml_protocol) { + err = amp_add_pentry(amp, xntag, d); + } else + if (parent==xml_field) { + err = amp_add_fentry(amp, f, xntag, d); + } + if (err) { + if (!quiet) { + amperr2str(err, errmsg); + fprintf(stderr, "WARNING: Automops found '%s' at XML position %i\n", errmsg, i); + } + return i; + } + } + } + if (i==(plen-1)) return 0; + + } while (l!=0); + return 0; +} + + + + + + + + + + + + + + + + +/////////////////////////////////////////////////////////////////////////////// +// // +////////////// ONLY XML NESTING STACK FUNCTIONS BELOW THIS LINE /////////////// +// +// + +void xnstack_init(struct xnstack *s) +{ + s->cursize=0; +} + +// Returns top data element or -1 if stack empty +// Does NOT remove data elements! +int xnstack_get_top(struct xnstack *s) +{ + if (s->cursize==0) return -1; + return s->data[s->cursize-1]; +} + +// Push data onto stack +// Returns -1 if max stack depth exceeded +int xnstack_push(struct xnstack *s, int d) +{ + if (s->cursize<XN_MAX_STACK) + s->data[s->cursize++]=d; + else + return -1; + return 0; +} + + +// Returns top data element and ALSO REMOVES it from stack +// Returns -1 if stack is empty +int xnstack_pop(struct xnstack *s) +{ + int d; + d=xnstack_get_top(s); + if (d>=0) s->cursize--; + return d; +} + +int xnstack_size(struct xnstack *s) +{ + return s->cursize; +} + diff --git a/staging/rcv_rtp.c b/staging/rcv_rtp.c new file mode 100644 index 0000000..336a6e0 --- /dev/null +++ b/staging/rcv_rtp.c @@ -0,0 +1,769 @@ +/* + * 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 + * +*/ + + +/////////////////////////////////////////////////// +// +// Table of contents: +// +// rcv_rtp_init() +// rcv_rtp() +// compare4B() +// got_rtp_packet() +// print_jitterbar() +// + +/////////////////////////////////////////////////// +// +// Documentation about RTP traffic analysis +// +// See http://wiki.wireshark.org/RTP_statistics +// +// + +#include "mz.h" +#include "mops.h" + +// Initialize the rcv_rtp process: Read user parameters and initialize globals +int rcv_rtp_init() +{ + char argval[MAX_PAYLOAD_SIZE]; + char dummy[512]; + int len; + u_int32_t port = 30000; // 4-byte variable to catch errors, see below + + int ssrc_s = 0; + + // Help text + + if (getarg(tx.arg_string,"help", NULL)==1) { + fprintf(stderr,"\n" + MAUSEZAHN_VERSION + "\n" + "| RTP reception for jitter measurements.\n" + "|\n" + "| Parameters:\n" + "|\n" + "| bar ...... Display modes: By default 'bar' is used and shows the RFC 3550 jitter as\n" + "| ASCII-based waterfall diagram.\n" + "| txt ...... The 'txt' mode prints all measurement values numerically upon each\n" + "| measurement interval.\n" +// "| curse ...... Shows all values and a diagram within an resizesable ncurses window.\n" + "|\n" + "| ssrc ....... Listen to the stream with the specified SSRC. You must specify this\n" + "| when there are concurrent streams, e. g. one in each direction.\n" + "|\n" + "| log ....... Write moving average also in a datafile (not only on terminal).\n" + "| logg ....... Like log but additionally write detailed real-time statistics in a data file\n" + "| path = <path> ....... Path to directory where datafiles can be stored (default: local directory).\n" + "| num = <10-%d> ...... number of packets to be received for averaging (default: %d).\n" + "| port = <0-65535> ....... Change if RTP packets are sent to a different port than 30000 (default).\n" + "|\n" + "| Note:\n" + "|\n" + "| Mausezahn can log actual realtime measurement data in data files (in the specified path or\n" + "| current directory) but always prints the moving average on the command line (this can be disabled\n" + "| using the 'quiet' option (-q)).\n" + "|\n" + "| The realtime data file(s) consist of two columns:\n" + "|\n" + "| 1. relative timestamp in usec\n" + "| 2. 'true' jitter in usec\n" + "|\n" + "| where the 'true' jitter is calculated using the (relative) timestamps inside the received\n" + "| packets t(i) and the (relative) timestamps T(i) observed locally when packets are received using\n" + "| the formula:\n" + "|\n" + "| jitter(i) = [T(i) - T(i-1)] - [t(i) - t(i-1)] + jitter(i-1) .\n" + "|\n" + "| This method has two advantages: (i) we do not need to synchronize the clocks of sender and\n" + "| receiver, and (ii) the TX-side jitter (mainly caused by the kernel-scheduler) is subtracted\n" + "| so that we primarily measure the jitter caused by the network.\n" + "| \n" + "| The data files consist of seven columns:\n" + "| \n" + "| 1. relative timestamp in seconds\n" + "| 2. minimum jitter\n" + "| 3. average jitter\n" + "| 4. minimum jitter\n" + "| 5. estimated jitter variance according RFC-3550\n" + "| 6. packet drop count (total)\n" + "| 7. packet disorder count (total)\n" + "| \n" + "| All measurement values are done in usec and refer to the current set of samples (see parameter 'num').\n" + "| Note that an RFC-conform jitter (smoothed mean deviation) is calculated and collected in column five.\n" + "| The drop value refers to the current measurement window, while the total drop and disorder values are\n" + "| calculated using some weird estimation functions; the goal was to provide a 'time-less' estimation\n" + "| while being able to automatically resynchronize to a re-started RTP measurement stream.\n" + "| \n" + "| EXAMPLE USAGE:\n" + "|\n" + "| At the TX-station enter:\n" + "|\n" + "| # mz eth0 -t rtp -B 10.3.3.42 (optionally change rate via -d option, payload size via pld command)\n" + "|\n" + "| At the RX-station (10.3.3.42) enter:\n" + "|\n" + "| # mz eth0 -T rtp \"log, path=/tmp/mz/\"\n" + "|\n" + "\n", TIME_COUNT_MAX, TIME_COUNT); + exit(0); + } + + + // check argstring for arguments + + if (getarg(tx.arg_string,"bar", NULL)==1) { + rtp_dm = BAR; + } + + if (getarg(tx.arg_string,"txt", NULL)==1) { + rtp_dm = TEXT; + } + + if (getarg(tx.arg_string,"curses", NULL)==1) { + rtp_dm = BAR; //NCURSES; + fprintf(stderr, " XXX This Mausezahn version does not support ncurses windows.\n"); + } + + if (getarg(tx.arg_string,"width", argval)==1) { + if (rtp_dm != BAR) { + fprintf(stderr, " mz/rcv_rtp: The 'width' parameter requires the display mode 'bar'\n"); + return -1; + } + bwidth = (int) str2int(argval); // [TODO] bwidth is currently not used + if (bwidth>RCV_RTP_MAX_BAR_WIDTH) { + fprintf(stderr, "The width must not exceed %i\n", + RCV_RTP_MAX_BAR_WIDTH); + return -1; + } + } else bwidth=80; + + if (getarg(tx.arg_string,"ssrc", argval)==1) { + ssrc_s = str2hex(argval, mz_ssrc, 4); + if (ssrc_s<0) { + fprintf(stderr, " mz/rtp_rcv: invalid ssrc!\n"); + return -1; + } + } + + if (getarg(tx.arg_string,"log", NULL)==1) { + rtp_log = 1; + } + + if (getarg(tx.arg_string,"logg", NULL)==1) { + rtp_log = 2; + } + + + if (getarg(tx.arg_string,"path", argval)==1) { + len = strlen(argval); + if (len>128) { + fprintf(stderr, " mz/Error: path must not exceed 128 characters!\n"); + exit (-1); + } + if (argval[len-1]!='/') { + strncat(argval, "/",1); // ensure that all paths end with "/" + } + strncpy(path, argval, 128); + } + + + if (getarg(tx.arg_string,"num", argval)==1) { + gind_max = (u_int32_t) str2int(argval); + if (gind_max > TIME_COUNT_MAX) { + gind_max = TIME_COUNT_MAX; + fprintf(stderr, " mz/Warning: num range is 10..%d. Will reset to %d.\n", + TIME_COUNT_MAX, TIME_COUNT_MAX); + } + else if (gind_max < 10) { + gind_max = 10; + fprintf(stderr, " mz/Warning: num range is 10..%d. Will reset to 10.\n", + TIME_COUNT_MAX); + } + } + + + // initialize global filter string + strncpy (rtp_filter_str, "udp dst port 30000", 64); + + if (getarg(tx.arg_string,"port", argval)==1) { + port = (u_int32_t) str2int(argval); + if (port>65535) { + port = 30000; + fprintf(stderr, " mz: Too large port number! Reset to default port (30000).\n"); + } + + sprintf(rtp_filter_str, "udp dst port %u", (unsigned int) port); + } + + // + if (ssrc_s==0) str2hex("ca:fe:fe:ed", mz_ssrc, 4); + + // open file + // + if (rtp_log) { + // get a new filename + timestamp_human(filename, "rtp_avg_"); + strncpy(dummy, path, 128); + strncat(dummy, filename, 64); + if (verbose) fprintf(stderr, " mz: Will open %s\n", dummy); + + fp = fopen (dummy, "w+"); + + if (fp == NULL) { + perror("fopen"); + exit (-1); + } + + gtotal=0; // counts written data blocks + fprintf(fp, "# Average jitter measurements made by Mausezahn " MAUSEZAHN_VERSION_SHORT ".\n"); + fprintf(fp, "# Timestamp is in seconds, all other values in microseconds.\n"); + fprintf(fp, "# Column values (from left to right):\n"); + fprintf(fp, "# 1. Timestamp\n" + "# 2. min_jitter\n" + "# 3. avg_jitter\n" + "# 4. max_jitter\n" + "# 5. estimated jitter according RFC-3550\n" + "# 6. packet drop count (total)\n" + "# 7. packet disorder count (total)\n"); + + + ///////////// also detailed log required ///////////// + if (rtp_log==2) { + // get a new filename + timestamp_human(filename, "rtp_rt_"); + strncpy(dummy, path, 128); + strncat(dummy, filename, 64); + if (verbose) fprintf(stderr, " mz: Will open %s\n", dummy); + + fp2 = fopen (dummy, "w+"); + + if (fp2 == NULL) { + perror("fopen"); + exit (-1); + } + + fprintf(fp2, "# Jitter measurements by Mausezahn " MAUSEZAHN_VERSION_SHORT ".\n"); + fprintf(fp2, "# Timestamp (usec) , true jitter (nsec)\n"); + } + + } + + drop=0; + dis=0; + jitter_rfc=0; + + return 0; +} + + + + + + + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// Defines the pcap handler and the callback function +int rcv_rtp() +{ + char errbuf[PCAP_ERRBUF_SIZE]; + + pcap_t *p; + + struct bpf_program filter; + + + + p = pcap_open_live (tx.device, + MAXBYTES_TO_READ, // max num of bytes to read + 0, // 1 if promiscuous mode + PCAP_READ_TIMEOUT_MSEC, // read timeout in msec + errbuf); + + if (p == NULL) + { + fprintf(stderr," mz/rcv_rtp: %s\n",errbuf); + exit(1); + } + + + if ( pcap_compile(p, + &filter, // the compiled version of the filter + rtp_filter_str, // text version of filter + 0, // 1 = optimize + 0) // netmask + == -1) + { + fprintf(stderr," mz/rcv_rtp: Error calling pcap_compile\n"); + exit(1); + } + + + + if ( pcap_setfilter(p, &filter) == -1) + { + fprintf(stderr," mz/rcv_rtp: Error setting filter\n"); + pcap_geterr(p); + exit(1); + } + + again: + + + pcap_loop (p, + 1, // number of packets to wait + got_rtp_packet, // name of callback function + NULL); // optional additional arguments for callback function + + + goto again; + + + // TODO: Currently we never reach this point! + fprintf(stderr, " mz: receiving of RTP finished.\n"); + pcap_close(p); + + return 0; +} + + + + +// Compares two 4-byte variables byte by byte +// returns 0 if identical, 1 if different +inline int compare4B (u_int8_t *ip1, u_int8_t *ip2) +{ + if (*ip1 != *ip2) return 1; + if (*(ip1+1) != *(ip2+1)) return 1; + if (*(ip1+2) != *(ip2+2)) return 1; + if (*(ip1+3) != *(ip2+3)) return 1; + + return 0; +} + + + + + +// Handler function to do something when RTP messages are received +void got_rtp_packet(u_char *args, + const struct pcap_pkthdr *header, // statistics about the packet (see 'struct pcap_pkthdr') + const u_char *packet) // the bytestring sniffed +{ + const struct struct_ethernet *ethernet; + const struct struct_ip *ip; + const struct struct_udp *udp; + const struct struct_rtp *rtp; + + int size_ethernet = sizeof(struct struct_ethernet); + int size_ip = sizeof(struct struct_ip); + int size_udp = sizeof(struct struct_udp); + // int size_rtp = sizeof(struct struct_rtp); + // + ethernet = (struct struct_ethernet*)(packet); + ip = (struct struct_ip*)(packet+size_ethernet); + udp = (struct struct_udp*)(packet+size_ethernet+size_ip); + rtp = (struct struct_rtp*)(packet+size_ethernet+size_ip+size_udp); + + struct mz_timestamp + deltaTX, + deltaRX; + + u_int32_t + i, + jitter_abs, + jitter_avg, + jitter_max, + jitter_min, + curtime=0; + + int32_t ltemp; + + u_int8_t *x,*y; + + char dummy[256]; + char ts_hms[10]; + unsigned char *dum; + static u_int32_t drop_last=0, drop_prev=0; + int s1, s2; + + // check if the RTP packet is really from a Mausezahn instance: + if (compare4B((u_int8_t*) &rtp->ssrc, mz_ssrc)==0) { + // we got a valid RTP packet from a Mausezahn instance + // Get current SQNR and store it in 'sqnr_cur' in host byte order + x = (u_int8_t*) &rtp->sqnr; + y = (u_int8_t*) &sqnr_cur; + + *y = *(x+1); + y++; + *y = *x; + + ///////////////////////////////////////////////////////////////////// + // Packet drop and disorder detection: + if (sqnr0_flag) { + if (sqnr_next==sqnr_cur) { // correct SQNR received + sqnr_next++; + sqnr_last++; + } else if (sqnr_last>sqnr_cur) { // disordered sequence + dis++; + if (drop) drop--; // don't get below 0 + else { // drop reached zero: resync (restarted RTP stream?) + sqnr_last = sqnr_cur; + sqnr_next = (++sqnr_last); + dis=0; + } + } else { // packet drop + drop += (sqnr_cur-sqnr_next); + sqnr_last = sqnr_cur; + sqnr_next = (++sqnr_last); + } + } else { + // initial synchronization with observed SQNR: + sqnr_last = sqnr_cur; + sqnr_next = (++sqnr_last); + sqnr0_flag++; + } + // + ///////////////////////////////////////////////////////////////////// + + + // Get RX timestamp from pcap header + timeRX[gind].sec = header->ts.tv_sec; + timeRX[gind].nsec = header->ts.tv_usec *1000; + + // Get TX timestamp from the packet + mops_hton4((u_int32_t*) &rtp->time_sec, (u_int8_t*) &timeTX[gind].sec); + mops_hton4((u_int32_t*) &rtp->time_nsec, (u_int8_t*) &timeTX[gind].nsec); + +// printf("%li %li\n", (long int) timeTX[gind].sec, (long int) timeTX[gind].nsec); + + gind++; + + //////////////////////////////////////////////////////////////// + if (gind == gind_max) { // array full, now calculate statistics + gind=0; + gtotal++; + + jitter_avg = 0; + jitter_min = 0xffffffff; + jitter_max = 0; + + + /////////////////////////////////////////////////////// + // calculate deltas and jitters + for (i=2; i<gind_max; i++) { // omit the first 2 data + // entries because of + // artificial high TX-delta! + // + /////////////////////////////////////////////// + // calculate deltaTX and deltaRX + // + s1=timestamp_subtract (&timeTX[i], &timeTX[i-1], &deltaTX); + s2=timestamp_subtract (&timeRX[i], &timeRX[i-1], &deltaRX); + if (s1) fprintf(stderr, " *** ***\n"); + + // Then calculate the precise jitter by considering + // also TX-jitter: (pseudo)jitter = deltaRX - deltaTX, + // hence we have positive and negative jitter (delay + // deviations) jitter entries are in +/- nanoseconds + jitter[i] = (deltaRX.sec*1000000000L + deltaRX.nsec) + - (deltaTX.sec*1000000000L + deltaTX.nsec); + // Calculate RFC 3550 jitter estimation. According to + // that RFC the jitter should be measured in timestamp + // units; however currently Mausezahn uses nanoseconds. + // (If we want to solve this: G.711 timestamp units are + // 125 usec, so jitter/=125 would be sufficient, AFAIK) + ltemp = labs(jitter[i]) - jitter_rfc; + jitter_rfc += (ltemp>>4); + // Add previous pseudojitter to get the true jitter + // (See Documentation!) + jitter[i] += jitter[i-1]; + // + //////////////////////////////////////////////// + + + + + //////////////////////////////////////////////// + // Determine avg, min, and max jitter within this time frame: + jitter_abs = labs(jitter[i]); + jitter_avg += jitter_abs; + if (jitter_abs < jitter_min) jitter_min = jitter_abs; + if (jitter_abs > jitter_max) jitter_max = jitter_abs; + // + //////////////////////////////// + + /// PRINT IN FILE_2: Detailed jitter data /// + if (rtp_log==2) { + // Calculate relative timestamp for column 1 of the datafile + curtime = timeRX[i].sec*1000000+timeRX[i].nsec/1000; + if (time0_flag) { + curtime = curtime - time0; + } else { // this is only done once during the Mausezahn process + time0 = curtime; + time0_flag=1; + curtime = curtime - time0; + } + fprintf(fp2, "%lu, %li\n", + (long unsigned int) curtime, + (long int) jitter[i]); + fflush(fp2); // save everything immediately + // (CHECK if fsync() is additionally needed) + } + } // end for (i=2; i<gind_max; i++) + // + //////////////////////////////////////////////////////// + + + jitter_avg = jitter_avg / (gind_max-2); // average true jitter, always positive + + if (drop>=drop_prev) { // because the total drop count may decrease(!) if disordered packets appear lately + drop_last = drop - drop_prev; + drop_prev=drop; + } else drop_last=0; + + // PRINT ON CLI: statistics data + switch (rtp_dm) { + case TEXT: + dum = (unsigned char*) &ip->src; + fprintf(stdout, + "Got %u packets from host %u.%u.%u.%u: %lu lost (%lu absolute lost, %lu out of order)\n" + " Jitter_RFC (low pass filtered) = %li usec\n" + " Samples jitter (min/avg/max) = %lu/%lu/%lu usec\n", + gind_max, + *(dum),*(dum+1),*(dum+2),*(dum+3), + (long unsigned int) drop_last, + (long unsigned int) drop, + (long unsigned int) dis, + (long int) jitter_rfc/1000, + (long unsigned int) jitter_min/1000, + (long unsigned int) jitter_avg/1000, + (long unsigned int) jitter_max/1000); + break; + + case BAR: + print_jitterbar(jitter_rfc/1000, drop_last); + break; + + case NCURSES: // would be nice...? + break; + + default: + break; + } + + // Determine whether some packets got lost: + // + // + // + // + + + + /// PRINT IN FILE_1: statistics only /// + if (rtp_log) { + ts_hms[0]=0x00; + timestamp_hms (ts_hms); + fprintf(fp, + "%s, %lu, %lu, %lu, %li, %u, %u\n", + ts_hms, + (long unsigned int) jitter_min/1000, + (long unsigned int) jitter_avg/1000, + (long unsigned int) jitter_max/1000, + (long int) jitter_rfc/1000, + drop, + dis); + fflush(fp); + } + + + + // Open another file if current file reaches a limit + // + if ((rtp_log==2) && (gtotal>MAX_DATA_BLOCKS)) { // file big enough, + gtotal=0; + if (fclose(fp2) == EOF) { + perror("fclose"); + exit(1); + } + + if (verbose) + fprintf(stderr, " mz: %s written.\n",filename); + + timestamp_human(filename, "rtp_"); // get a new filename + strncpy(dummy, path, 128); + strncat(dummy, filename, 64); + + if (verbose) fprintf(stderr, " mz: Will open %s\n", dummy); + + if ( (fp2 = fopen (dummy, "w+")) == NULL) { + if (errno != EAGAIN) { + perror("fopen"); + exit (-1); + } + } + fprintf(fp2, "# Jitter measurements by Mausezahn " + MAUSEZAHN_VERSION_SHORT ".\n"); + fprintf(fp2, "# Timestamp (usec) , true jitter (nsec)\n"); + } + } // statistics end ********************************************************************* + } +} + + + + +void print_jitterbar (long int j, u_int32_t d) +{ + // Determine actual data window by considering two events: + // + // 1) window move (j exceeds lower or upper limit) + // 2) window rescale (window moves happen too often or the variance + // of successive data points is too small) + // + // The most critical value is the chosen resolution (window range), + // especially the _initial_ resolution. + + static long int range=0, min=0, max=0, minvar=0, j0=0, dj=0; + static int moved=0, varcount=0, barcount=0; + char str[128], bar[150], + str1[8], str2[8], str3[8], str4[8]; + int event=0, anz; + long int tmp; + + // Initialize vars (start with an opened window) + // Note that 'range' is actually half of the window + if (!range) { + range=j; + if (range<500) range=500; + max = j+range; + min = 0; + minvar=range/40; + event++; + } else { + dj = labs(j-j0); // no initialization: calculate jitter delta + } + + // Move window when borders crossed: + if ((j<min) || (j>max)) { + max = j + range; + min = max-2*range; + if (min<0) { + min=0; + range=(max-min)/2; + fprintf(stdout, "\nNOTE: +- Rescaled window to %4.2f msec\n", (double) range/500); + } + moved++; + event++; + fprintf(stdout,"\n"); +// printf("move event: min=%li max=%li\n", min, max); + } else { + if (moved) moved--; +// printf("normal event: min=%li max=%li\n", min, max); + } + + + // Increase range when window moved 5 times in a row + if (moved>2) { + range*=3; + if (range>10000000L) range=10000000L; + minvar=range/40; + if (minvar<1000) minvar=1000; + max=j+range; + min=j-range; + if (min<0) { + min=0; + range=(max-min)/2; + } + moved=0; + event++; +// printf("scale up event: min=%li max=%li\n", min, max); + fprintf(stdout, "\nNOTE: ++ Rescaled window to %4.2f msec\n", (double) range/500); + } + + + // Decrease range when jitter deltas are smaller than minvar + // 5 times in a row + if (dj<minvar) + varcount++; + else + varcount=0; + + if (varcount>5) { + range*=0.75; + if (range>j) range=j; + if (range<500) { + range=500; + } + minvar=range/40; + if (minvar<1000) minvar=1000; + max=j+range; + min=j-range; + if (min<0) { + min=0; + range=(max-min)/2; + } + fprintf(stdout, "\nNOTE: -- Rescaled window to %4.2f msec\n", (double) range/500); + varcount=0; + event++; +// printf("scale down event: min=%li max=%li\n", min, max); + } + + j0=j; + + barcount++; + if (barcount==24) { + event=1; + barcount=0; + } + + if (event) { + tmp=range*0.667; + sprintf(str1,"%4.2f", (double) min/1000); + sprintf(str2,"%4.2f", (double) (min+tmp)/1000); + sprintf(str3,"%4.2f", (double) (max-tmp)/1000); + sprintf(str4,"%4.2f", (double) max/1000); + + fprintf(stdout, + "%-6s %-6s %-6s %-6s\n" + "|-------------------------|-------------------------|-------------------------|\n", + str1, str2, str3, str4); + barcount=0; + } + + anz = 80*(j-min)/(2*range); + if (anz) { + memset((void*) str, '#', anz); + memset((void*) str+anz, ' ', 80-anz); + str[80]='\0'; + } + else { + memset((void*) str, ' ', 80); + str[0]='#'; + str[80]='\0'; + } + if (d) + sprintf(bar, "%s%4.2f msec !%lu dropped!", str, (double) j/1000, (unsigned long int) d); + else + sprintf(bar, "%s%4.2f msec", str, (double) j/1000); + + fprintf(stdout,"%s\n", bar); +} + diff --git a/staging/rtp.c b/staging/rtp.c new file mode 100644 index 0000000..6ce4458 --- /dev/null +++ b/staging/rtp.c @@ -0,0 +1,217 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008 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 + * +*/ + + + +#include "mz.h" +#include "cli.h" +#include "mops.h" + +#define MZ_RTP_HELP \ + "| RTP type: Send Real Time Protocol packets.\n" \ + "|\n" \ + "| This mode is solely intended to conduct delay, drop, and jitter measurements in\n" \ + "| Voice (Video) over IP networks. You will typically initiate another Mausezahn\n" \ + "| instance on the destination host, which will perform the measurements or even\n" \ + "| 'bounce back' the packets for Round Trip Time (RTT) measurements.\n" \ + "|\n" \ + "| When the delay parameter is not specified, the default (inter-packet) delay is\n" \ + "| set to 20 msec. You must specify the destination host using the -B option.\n" \ + "| The default destination port is (UDP) 30000 but can be overridden (dp parameter).\n" \ + "| You do not need to specify the count option (-c), because 'infinite' (0) is assumed.\n" \ + "|\n" \ + "| You can specify these additional GENERAL options:\n" \ + "|\n" \ + "| -c <count> ..... use this packet count value instead of infinity.\n" \ + "| -d <delay> ..... use this delay value instead of the defaul. Per default\n" \ + "| the units are microseconds but you can also use msec or sec\n" \ + "|\n" \ + "| You can specify these additional UDP/RTP-specific arguments:\n" \ + "|\n" \ + "| dp = <1-65535> ..... use this UDP destination port instead of 30,000.\n" \ + "| sp = <1-65535> ..... use this UDP source port instead of random.\n" \ + "| ssrc = XX:XX:XX:XX ... use this hex sequence as stream identifier\n" \ + "| (=SSRC, required for multiple concurrent measurements)\n" \ + "| codec ..... simulate G.711 codec (other will follow).\n" \ + "| pld = <1..1000> ....... create specified payload size (default=160 bytes, which results\n" \ + "| in a total datagram length of 180 bytes, considering the UDP and\n" \ + "| RTP header lengths (8 and 12 bytes, respectively).\n" \ + "|\n" \ + "| Additional help: enter 'mz -T rtp help'\n" \ + "|\n" + + + +int create_rtp_packet() +{ + u_int8_t byte1, byte2; + u_int16_t seqnr; + u_int8_t ssrc[4] = {0,0,0,0} ; + int ssrc_s = 0; + u_int8_t *ptr; + char argval[MAX_PAYLOAD_SIZE]; + unsigned int rtp_payload_size=160; + struct mz_timestamp ts; + + if ( (getarg(tx.arg_string,"help", NULL)==1) && (mode==RTP) ) { + if (mz_port) + { + cli_print(gcli, "%s", MZ_RTP_HELP); + return -1; + } + else + { + + fprintf(stderr,"\n" + MAUSEZAHN_VERSION + "\n%s", MZ_RTP_HELP); + exit(0); + } + } + + + if (getarg(tx.arg_string,"pld", argval)==1) { + rtp_payload_size = (unsigned int) str2int(argval); + } + + if (getarg(tx.arg_string,"codec", argval)==1) { + tx.delay = 20000; + } + + if (getarg(tx.arg_string,"ssrc", argval)==1) { + ssrc_s = str2hex(argval, ssrc, 4); + if (ssrc_s<0) { + fprintf(stderr, " mz/rtp: invalid ssrc!\n"); + return -1; + } + } + + // TODO: Optional arguments for RTP + + + // Create header: // + + // Byte 1 + // + // +--+--+--+--+--+--+--+--+ + // | ver | P| X| CSRC Count| + // +--+--+--+--+--+--+--+--+ + // + // Default: ver=2, Padding=0, Extension_Header=1, CSRC_Count=0 => 10 0 1 0000 = 0x90 + + byte1 = 0x90; + + // Byte 2 + // + // +--+--+--+--+--+--+--+--+ + // | M| Payload Type | + // +--+--+--+--+--+--+--+--+ + // + // Marker=0, Payload Type=0 (or 8 alternatively) + + byte2 = 0x00; + + // Bytes 3,4 + // + // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + // | Sequence Number | + // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + seqnr = 0x0000; + + // Bytes 5,6,7,8 + // + // Timestamp /* done below */ + // + + + // Bytes 9,10,11,12 + // + // Synchronization Source Identifier + // + + if (ssrc_s==0) str2hex("ca:fe:fe:ed", ssrc, 4); + + // Bytes 13,14,15,16 + // + // CSRC - Contributing Source Identifiers (optional, only used by mixers) + // + // csrc = 0x00000000; + + // Bytes 17,18,19,20 + // + // Header Extension (optional) NOT USED HERE! + // + + // !!! Thus payload begins with index 16 in a C array !!! + + // ------------ Now combine all fields: ---------------- + tx.udp_payload[0] = byte1; + tx.udp_payload[1] = byte2; + + ptr = (u_int8_t*) &seqnr; + tx.udp_payload[2] = *(ptr+1); + tx.udp_payload[3] = *ptr; + + // TIMESTAMP: will be linearly increased, e.g. using 20msec G.711: 0, 160, 320, ... + tx.udp_payload[4] = 0x00; + tx.udp_payload[5] = 0x00; + tx.udp_payload[6] = 0x00; + tx.udp_payload[7] = 0x00; + + tx.udp_payload[8] = ssrc[0]; + tx.udp_payload[9] = ssrc[1]; + tx.udp_payload[10] = ssrc[2]; + tx.udp_payload[11] = ssrc[3]; + + /* + ptr = (u_int8_t*) &csrc; + tx.udp_payload[12] = *(ptr+3); + tx.udp_payload[13] = *(ptr+2); + tx.udp_payload[14] = *(ptr+1); + tx.udp_payload[15] = *ptr; + */ + + // Add the NEW Mausezahn extension header (see mops_ext_rtp.c) + tx.udp_payload[12] = 0xca; // identifier + tx.udp_payload[13] = 0xca; + tx.udp_payload[14] = 0x00; + tx.udp_payload[15] = 0x04; // length + getcurtime(&ts); // Now add TX timestamp: + mops_hton4 ((u_int32_t*) &ts.sec, &tx.udp_payload[16]); + mops_hton4 ((u_int32_t*) &ts.nsec, &tx.udp_payload[20]); + // NOTE: The remaining 8 bytes of this extension header are set to zero + // via the following code. + + memset(&tx.udp_payload[24], 0x00, (rtp_payload_size-12)); // payload (considering our 8 byte timestamp) + tx.udp_payload_s = 12 + rtp_payload_size; // the latter ist the payload size + + // ---------- now hand over to UDP ----------------- + + tx.dp = 30000; + tx.sp = 30000; + + tx.udp_len = 8 + tx.udp_payload_s; + + return 0; +} + + + + + diff --git a/staging/send.c b/staging/send.c new file mode 100644 index 0000000..5ad1a20 --- /dev/null +++ b/staging/send.c @@ -0,0 +1,264 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008 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 + * +*/ + + + + + +// *************************************************************************** +// +// This sections contains: +// +// - complexity() ... calculates and reports how many frames will +// be generated. +// - send_frame() ... the general and mighty SENDING FUNCTION. +// +// *************************************************************************** + +#include "mz.h" +#include "cli.h" + + +// Calculates the number of frames to be sent. +// Should be used as standard output except the +// 'quiet' option (-q) has been specified. +int complexity() +{ + unsigned long int + nr_sqnr = 1, + nr_dp = 1, + nr_sp = 1, + nr_da = 1, + nr_sa = 1; + + u_int32_t + sn1, + sn2, + delta; + + long double ref; + + if (tx.count==0) goto infinity; + + total_d = 1.0; + + // How many sequence numbers? + if (tx.tcp_seq_delta) + { + sn1 = tx.tcp_seq_start; + sn2 = tx.tcp_seq_stop; + delta = tx.tcp_seq_delta; + + if (sn1<sn2) // the easier case + { + nr_sqnr = (sn2-sn1)/delta; + } + else + { + nr_sqnr = (sn2 + (0xffffffff - sn1)) / delta; + } + //fprintf(stderr,"SQNR Range = %lu\n",nr_sqnr); + nr_sqnr +=1; + } + + if (tx.dp_isrange) + { + nr_dp = tx.dp_stop - tx.dp_start + 1; + //fprintf(stderr,"DP Range = %lu\n",nr_dp); + } + + if (tx.sp_isrange) + { + nr_sp = tx.sp_stop - tx.sp_start + 1; + //fprintf(stderr,"SP Range = %lu\n",nr_sp); + } + + if (tx.ip_dst_isrange) + { + nr_da = tx.ip_dst_stop - tx.ip_dst_start + 1; + //fprintf(stderr,"DA Range = %lu\n",nr_da); + } + + if (tx.ip_src_isrange) + { + nr_sa = tx.ip_src_stop - tx.ip_src_start + 1; + //fprintf(stderr,"SA Range = %lu\n",nr_sa); + } + + total_d *= tx.count; + total_d *= nr_sqnr; + total_d *= nr_dp; + total_d *= nr_sp; + total_d *= nr_da; + total_d *= nr_sa; + + + + + ref=0xffffffff; + + ref*=ref; + + if (total_d>ref) + { + fprintf(stderr, "You must be crazy...\n"); + } + else if (total_d>0xffffffff) + { + fprintf(stderr, "Do you REALLY know what you do?\n"); + } + else if (total_d>0xffffff) + { + fprintf(stderr, "Do you know what you do?\n"); + } + + if (mz_port) + { + cli_print(gcli, "Mausezahn will send %.Lf frames...\r", total_d); + } + else + { + fprintf(stderr, "Mausezahn will send %.Lf frames... ", total_d); + fflush(stderr); + if (verbose) fprintf(stderr,"\n"); + } + + + + mz_start = clock(); + + infinity: + + + if (tx.count==0) + { + if (mz_port) + { + cli_print(gcli, "Mausezahn will send frames infinitly...\n"); + } + else + { + fprintf(stderr, "Mausezahn will send frames infinitly...\n"); + } + } + + + return 0; +} + + + +/////////////////////////////////////////////////////////////////////// +// +// Send complete frame (layers 2, 3, 4) multiple times if required +// +// +int send_frame (libnet_t *l, libnet_ptag_t t3, libnet_ptag_t t4) +{ + int i=0, count; + + int // local vars are faster ;-) + tcp_seq_delta, + dp_isrange, + sp_isrange, + ip_dst_isrange, + ip_src_isrange, + rtp_mode=0; + + + count = tx.count; + tcp_seq_delta = tx.tcp_seq_delta; + dp_isrange = tx.dp_isrange; + sp_isrange = tx.sp_isrange; + ip_dst_isrange = tx.ip_dst_isrange; + ip_src_isrange = tx.ip_src_isrange | tx.ip_src_rand; + if (mode == RTP) rtp_mode = 1; + + if (count==0) goto AGAIN; + + for (i=0; i<count; i++) + { + + AGAIN: + + if (verbose) (void) print_frame_details(); + libnet_write(l); + if (mz_rand) tx.delay=(unsigned int) tx.delay*rand()/RAND_MAX; + if (tx.delay) SLEEP (tx.delay); + + // No layer-2 modifications done here + // (see create_eth_frame which does L2 modifications additionally) + + + if (tcp_seq_delta) + { + if (update_TCP_SQNR(l, t4)==0) // end of range not yet reached + { + goto AGAIN; + } + } + + if (dp_isrange) + { + if (update_DPORT(l, t4)==0) // end of range not yet reached + { + goto AGAIN; + } + } + + if (sp_isrange) + { + if (update_SPORT(l, t4)==0) // end of range not yet reached + { + goto AGAIN; + } + } + + + if (ip_dst_isrange) + { + if (update_IP_DA(l, t3)==0) // end of range not yet reached + { + goto AGAIN; + } + } + + if (ip_src_isrange) // also catches random SA (see above) + { + if (update_IP_SA(l, t3)==0) // end of range not yet reached + { + goto AGAIN; + } + } + + if (rtp_mode) // update SQNR and Timestamps in RTP header and payload + { + update_RTP(l, t4); + } + + + if (!count) goto AGAIN; + } + + + + libnet_destroy(l); + + return 0; +} + diff --git a/staging/send_eth.c b/staging/send_eth.c new file mode 100644 index 0000000..ae811e8 --- /dev/null +++ b/staging/send_eth.c @@ -0,0 +1,491 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008 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 + * +*/ + + + + +// *************************************************************************** +// This sections contains (as alternative to 'send_frame' in send.c) +// a layer-2 based flexible sending function. +// +// Layer-2 modifications such as 802.1Q and MPLS is considered here! +// +// *************************************************************************** + + + +#include "mz.h" + +libnet_ptag_t create_eth_frame (libnet_t *l, libnet_ptag_t t3, libnet_ptag_t t4) +{ + libnet_t *L=NULL; + char errbuf[LIBNET_ERRBUF_SIZE]; + libnet_ptag_t t=0, tmpls; + char argval[128]; + u_int8_t et[2]; + int et_len; + + int i=0, j, mlen, mkomma, len, count, offset=0, found_colon=0; + char *left, *right; + char *f, mtag[64]; + char verbose_mpls_string[128]; + + u_int8_t *packet; + u_int32_t packet_s; + + char *saveptr, *ptrsubstring, substring[16], tmp[4*MAX_8021Q_TAGS]; + u_int8_t CoS; // 0..7 + u_int16_t vlan; // 12 bit value (0..4095) + u_int8_t dot1Q[4*MAX_8021Q_TAGS], *ptr; + u_int16_t dot1Q_eth_type=0x8100; + int bytecnt=0; + + int isdot1Q, tcp_seq_delta, dp_isrange, sp_isrange, ip_dst_isrange, ip_src_isrange, eth_src_rand, rtp_mode=0; + unsigned int delay; + + + + + + + //////////////////////////////////////////////////// + // Prepare MPLS header if required + if (tx.mpls) + { + // first check how many labels have been specified: + mlen = strlen (tx.mpls_txt); + mkomma=0; + + for (i=0; i<mlen; i++) + { + if (tx.mpls_txt[i]==',') mkomma++; + } + + f = strtok_r (tx.mpls_txt, ",", &saveptr); + + tx.mpls_bos=1; + + do + { + strncpy(mtag, f, 64); + /* + if (mkomma==0) + { + tx.mpls_bos=0; + } + else + { + printf("BOS=1\n"); + tx.mpls_bos=1; + } + */ + + + if ( get_mpls_params(mtag) ) // error? + { + fprintf(stderr," mz/get_mpls_params: MPLS Parameters problem.\n"); + exit (0); + } + + tmpls = libnet_build_mpls(tx.mpls_label, + tx.mpls_exp, + tx.mpls_bos, + tx.mpls_ttl, + NULL, + 0, + l, + 0); + + if (tmpls == -1) + { + fprintf(stderr, " mz/create_ip_packet: Can't build MPLS header: %s\n", libnet_geterror(l)); + exit (0); + } + + if (verbose) + { + sprintf(verbose_mpls_string,"[%u:%u:%u:%u]", + tx.mpls_label, + tx.mpls_exp, + tx.mpls_bos, + tx.mpls_ttl); + strcat(tx.mpls_verbose_string, verbose_mpls_string); + strcat(tx.mpls_verbose_string, " "); + } + + tx.mpls_bos=0; + mkomma--; + } + while ( (f=strtok_r(NULL, ",", &saveptr)) != NULL); + + } + + + + + + + + //////////////////////////////////////////////////////////////////////////////////////////// + // Evaluate Ethernet CLI options (-a and -b) + if (check_eth_mac_txt(ETH_DST)) // if true then problem (invalid user input?) + { + str2hex("ff:ff:ff:ff:ff:ff",tx.eth_dst, 6); // the default + } + + // if not specified then own MAC will be used automatically + (void) check_eth_mac_txt(ETH_SRC); + + + // Get CLI arguments: + // If NOT set, default: 0x800 or ETHERTYPE_MPLS if MPLS is used (see init.c) + if (getarg(tx.arg_string,"ether_type", argval)==1) + { + et_len = str2hex (argval, et, 2); + + if (et_len==1) + tx.eth_type = et[0]; + else // 2 bytes specified + tx.eth_type = 256 * et[0] + et[1]; + + //tx.eth_type = (u_int16_t) str2int(argval); + } + + + + + + + + + ///////////////////////////////////////////////////////////////////////////////// + // Ethernet with 802.1Q + // + // If multiple 802.1Q tags are specified then we need to build the whole + // frame manually because libnet only supports adding a single VLAN header. + // The easiest solution is to create the hex-string of the 802.1Q-chain as + // u_int8_t QinQ[] then add the IP packet as payload... + // + if (tx.dot1Q) // actually contains the number of VLAN tags + { + + // we want our own context! + L = libnet_init(LIBNET_LINK_ADV, tx.device, errbuf); + if (L == NULL) + { + fprintf(stderr, "%s", errbuf); + exit(EXIT_FAILURE); + } + + strncpy(tmp,tx.dot1Q_txt,(4*MAX_8021Q_TAGS)); + ptrsubstring = strtok_r(tmp, ",.", &saveptr); + bytecnt=0; + do + { + // make a local copy + strncpy(substring, ptrsubstring, 16); + CoS=0; vlan=0; + // Get CoS and VLAN ID from partial string + len = strlen(substring); + found_colon=0; + for (i=0; i<len; i++) + { + if (substring[i]==':') found_colon=1; + } + if (found_colon) // Both CoS and VLAN specified + { + left = strtok (substring, ":"); + right = strtok (NULL, ":"); + CoS = (u_int8_t) str2int (left); + vlan = (u_int16_t) str2int (right); + } + else // Only VLAN specified + { + vlan = (u_int16_t) str2int (substring); + } + + if (CoS > 7) + { + fprintf(stderr, " mz/create_eth_frame: CoS too high, adjusted to 7\n"); + CoS = 7; + } + + if (vlan > 4095) + { + fprintf(stderr, " mz/create_eth_frame: VLAN number too high, adjusted to 4095\n"); + vlan = 4095; + } + + // create 4 byte 802.1Q header: + + dot1Q[bytecnt+0]=0x81; + dot1Q[bytecnt+1]=0x00; + ptr = (u_int8_t*) &vlan; + dot1Q[bytecnt+3]=*ptr; + ptr++; + *ptr = *ptr ^ (CoS<<5); // add CoS + dot1Q[bytecnt+2]=*ptr; + //check: + //printf("%02x %02x %02x %02x\n",dot1Q[bytecnt+0],dot1Q[bytecnt+1],dot1Q[bytecnt+2],dot1Q[bytecnt+3]); + bytecnt+=4; // next tag (note that bytecnt will finally hold the number of used bytes!) + + } while ( (ptrsubstring = strtok_r(NULL, ",.", &saveptr)) !=NULL); //get all VLAN tags + + // now create the whole packet: + + dot1Q_eth_type = 0x8100; //these are also the first two bytes of dot1Q[] + bytecnt = bytecnt-2; + + for (i=0;i<bytecnt;i++) + { + tx.eth_payload[i]=dot1Q[i+2]; + } + + // now add official EtherType for the payload (this has been determined some lines above) + ptr = (u_int8_t*) & tx.eth_type; + tx.eth_payload[i+1]= *ptr; + ptr++; + tx.eth_payload[i]= *ptr; + offset = i+2; + + // - + // -- + // --- + // ---- now all 802.1Q headers are genereated ---- + // ---- and are placed already in tx.eth_payload ---- + // ---- (and 'i' points to the next free byte) ---- + // --- + // -- + // - + + // Finally get all bytes of upper layers (IP packet and payload) + if (libnet_adv_cull_packet(l, &packet, &packet_s) == -1) + { + fprintf(stderr, "%s", libnet_geterror(l)); + } + + // Copy the upper layer data to the eth_payload + for (j=0; j<packet_s; j++) + { + tx.eth_payload[j+offset]=packet[j]; + } + + // 'libnet_adv_cull_packet' performs an implicit malloc() and a corresponding call + // to libnet_adv_free_packet() should be made to free the memory packet occupies: + libnet_adv_free_packet(l, packet); + + tx.eth_payload_s = j+offset; + tx.eth_type = dot1Q_eth_type; + + t = libnet_build_ethernet (tx.eth_dst, + tx.eth_src, + tx.eth_type, + tx.eth_payload, + tx.eth_payload_s, + L, + 0); + + if (t == -1) + { + fprintf(stderr, " mz/create_eth_frame: Can't build Ethernet header: %s\n", + libnet_geterror(l)); + exit(EXIT_FAILURE); + } + + // NOW the whole frame is ready to send! + + } + + else // normal Ethernet header without any 802.1Q-tag or MPLS-label + + { + + t = libnet_build_ethernet (tx.eth_dst, + tx.eth_src, + tx.eth_type, + NULL, // the payload + 0, + l, + 0); + + if (t == -1) + { + fprintf(stderr, " mz/create_eth_frame: Can't build Ethernet header: %s\n", + libnet_geterror(l)); + exit(EXIT_FAILURE); + } + } + + ///////////////////////////////////////////////////////////////////////////// + // + // Now send everything - maybe lots of times with modifications. + // + // + + // local vars are faster :-) + count = tx.count; + delay = tx.delay; + eth_src_rand = tx.eth_src_rand; + tcp_seq_delta = tx.tcp_seq_delta; + dp_isrange = tx.dp_isrange; + sp_isrange = tx.sp_isrange; + ip_dst_isrange = tx.ip_dst_isrange; + ip_src_isrange = tx.ip_src_isrange | tx.ip_src_rand; // either case should call update_SA() + isdot1Q = tx.dot1Q; + if (mode == RTP) rtp_mode = 1; + + if (count==0) goto AGAIN; + + for (i=0; i<count; i++) + { + + AGAIN: + + if (isdot1Q) + { + // Get all bytes of upper layers (IP packet and payload) + if (libnet_adv_cull_packet(l, &packet, &packet_s) == -1) + { + fprintf(stderr, "%s", libnet_geterror(l)); + } + + // Copy the upper layer data to the eth_payload + for (j=0; j<packet_s; j++) + { + tx.eth_payload[j+offset]=packet[j]; + } + + // 'libnet_adv_cull_packet' performs an implicit malloc() and a corresponding call + // to libnet_adv_free_packet() should be made to free the memory packet occupies: + libnet_adv_free_packet(l, packet); + + if (eth_src_rand) update_Eth_SA(L, t); + + t = libnet_build_ethernet (tx.eth_dst, + tx.eth_src, + tx.eth_type, + tx.eth_payload, + tx.eth_payload_s, + L, + t); + if (t == -1) + { + fprintf(stderr, " mz/create_eth_frame: Can't build Ethernet header: %s\n", + libnet_geterror(l)); + exit(EXIT_FAILURE); + } + if (verbose) (void) print_frame_details(); + libnet_write(L); + } + else // No QinQ and/or MPLS modifications => use normal 'l' context: + { + if (eth_src_rand) update_Eth_SA(l, t); + if (verbose) (void) print_frame_details(); + libnet_write(l); + } + + +// if (verbose) (void) print_frame_details(); + if (delay) SLEEP (delay); + + + if (tcp_seq_delta) + { + if (update_TCP_SQNR(l, t4)==0) // end of range not yet reached + { + goto AGAIN; + } + } + + if (dp_isrange) + { + if (update_DPORT(l, t4)==0) // end of range not yet reached + { + goto AGAIN; + } + } + + if (sp_isrange) + { + if (update_SPORT(l, t4)==0) // end of range not yet reached + { + goto AGAIN; + } + } + + if (ip_dst_isrange) + { + if (update_IP_DA(l, t3)==0) // end of range not yet reached + { + goto AGAIN; + } + } + + if (ip_src_isrange) + { + if (update_IP_SA(l, t3)==0) // end of range not yet reached + { + goto AGAIN; + } + } + + + if (rtp_mode) // update SQNR and Timestamps in RTP header and payload + { + update_RTP(l, t4); + } + + + if (!count) goto AGAIN; + } + + + libnet_destroy(l); + if (isdot1Q) + libnet_destroy(L); + + + return t; +} + + + +void print_dot1Q_help(void) +{ + + fprintf(stderr,"\n" + MAUSEZAHN_VERSION + "\n" + "| 802.1Q header Syntax: -Q tag[,tag[,tag[,...]]]\n" + "| where each tag may consist of a CoS value using the syntax:\n" + "|\n" + "| <CoS>:<tag value>\n" + "|\n" + "| Examples:\n" + "|\n" + "| # mz -Q 100\n" + "| # mz -Q 5:100\n" + "| # mz -Q 5:100,200\n" + "| # mz -Q 5:100,7:200\n" + "| # mz -Q 100,200,300,5:400\n" + "\n\n"); + + exit(0); +} + + diff --git a/staging/syslog.c b/staging/syslog.c new file mode 100644 index 0000000..c8fac9b --- /dev/null +++ b/staging/syslog.c @@ -0,0 +1,248 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008,2009 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 + * +*/ + + +#include "mz.h" +#include "cli.h" + +#define MZ_SYSLOG_HELP \ + "| Syslog type: Send (traditional) Syslog packets via UDP.\n" \ + "|\n" \ + "| Parameters:\n" \ + "|\n" \ + "| severity, sev 0-7 .... Severity level from Emergency (0) to Debug (7)\n" \ + "| facility, fac 0-23 .... Facility number\n" \ + "|\n" \ + "| time hh:mm:ss .... Local time, 24-hour format\n" \ + "| month, mon Mmm .... Current month, 1-12\n" \ + "| day dd .... Current day, 0-31\n" \ + "|\n" \ + "| host max 314 bytes .... Name or IP Address of sending host\n" \ + "|\n" \ + "| Defaults:\n" \ + "|\n" \ + "| Per default the severity \"Warning\" (4), the facility \"Security\" (4), and the\n" \ + "| current time stamp is used. If no host is given, host is set to \"MZ\"\n" \ + "|\n" \ + "| You can define the Syslog message itself using the -P flag. For example:\n" \ + "|\n" \ + "| mz eth0 -t syslog sev=3 -P \"You have been mausezahned.\"\n" \ + "|\n" \ + "| By the way, mz (by intention) does not check if your timestamp is valid according\n" \ + "| calendar rules. It is generally recommended to follow the Darwin Era Calendar ;-)\n" \ + "|\n" + + + +// RFC 3164 states that a Syslog message consists of three parts: PRI, HEADER, and MSG. +// +// 1) PRI: contains facility(f) and severity(s), using the syntax "<N>" where N = f * 8 + s +// +// 2) HEADER: contains a timestamp and a sender-ID (name or IP), for example "May 25 23:42:42 Mausezahnhost" +// Note that instead of leading zeroes a space must be used for the day e. g. "May 5". +// However leading zeroes are required for hour, minutes, seconds, e. g. "01:05:09" +// +// 3) MSG: consists of TAG and CONTENT field. The TAG identifies the program or process and +// must not exceed 32 characters. Typically the TAG and the CONTENT fields are delimited +// via either a "[", or a colon (:) or a space. The CONTENT field is a simple text. +// +// EXAMPLE from RFC 3164: +// +// <34>Oct 11 22:14:15 mymachine su: 'su root' failed for lonvick on /dev/pts/8 +// +// EXAMPLE from Cisco Router: +// +// *Mar 23 13:45:08.727: %ENVMON-3-FAN_FAILED: Fan 2 not rotating +// + + +int create_syslog_packet() +{ + unsigned int pri, sev, fac, day, curday, mon, curmon; + char lt[8], host[314]; + char *Months[12] = + { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + + + time_t curtime; + struct tm curtime_broken; + char argval[MAX_PAYLOAD_SIZE]; + int ca=0, aa; + + aa=number_of_args(tx.arg_string); + + if ( (getarg(tx.arg_string,"help", NULL)==1) && (mode==SYSLOG) ) + { + ca++; // counts each argument + if (mz_port) + { + cli_print(gcli, "%s", MZ_SYSLOG_HELP); + return -1; + } + else + { + fprintf(stderr,"\n" + MAUSEZAHN_VERSION + "\n%s", MZ_SYSLOG_HELP); + + exit(0); + } + } + + + if ( (getarg(tx.arg_string,"severity", argval)==1) || + (getarg(tx.arg_string,"sev", argval)==1) ) + { + ca++; // counts each argument + sev = (unsigned int) str2int(argval); + } + else + { + sev = 4; + } + + if ( (getarg(tx.arg_string,"facility", argval)==1) || + (getarg(tx.arg_string,"fac", argval)==1) ) + { + ca++; // counts each argument + fac = (unsigned int) str2int(argval); + } + else + { + fac = 4; + } + + + time(&curtime); + localtime_r (&curtime, &curtime_broken); + + + + if (getarg(tx.arg_string,"time", argval)==1) + { + ca++; // counts each argument + strncpy(lt,argval,8); + // TODO: check if specified timestamp has valid format, e. g. 15:03:22 + } + else + { + timestamp_hms (lt); + } + + + + curmon = curtime_broken.tm_mon; // Note that Jan = 0, ..., Dec = 11 !!! + + if ( (getarg(tx.arg_string,"month", argval)==1) || + (getarg(tx.arg_string,"mon", argval)==1) ) + { + ca++; // counts each argument + mon = (unsigned int) str2int(argval); + if ( (mon<1) || (mon>12) ) + { + fprintf(stderr, " mz/syslog: Invalid month; will use current month (%i)!\n", curmon+1); + mon = curmon; + } + } + else + { + mon = curmon; + } + + curday = curtime_broken.tm_mday; + + if (getarg(tx.arg_string,"day", argval)==1) + { + ca++; // counts each argument + day = (unsigned int) str2int(argval); + if ( (day<1) || (day>31) ) + { + fprintf(stderr, " mz/syslog: Invalid day; will use current day(%i)!\n", curday); + day = curday; + } + } + else + { + day = curday; + } + + + if (getarg(tx.arg_string,"host", argval)==1) + { + ca++; // counts each argument + strncpy(host,argval,314); // 314 is just an arbitrary number ;-) + } + else + { + strcpy(host, "MZ42"); + } + + + // CHECK SURPLUS ARGUMENTS + if (aa!=ca) { + fprintf(stderr, "WARNING: %i unmatched arguments within argument string!\n", aa-ca); + } + + + // Now put everything together: + // + // Again the EXAMPLE from RFC 3164: + // + // <34>Oct 11 22:14:15 mymachine su: 'su root' failed for lonvick on /dev/pts/8 + // + + + pri = 8*fac+sev; + + sprintf((char*) tx.udp_payload, "<%d>%s %2i %s %s ", + pri, + Months[mon], + day, + lt, + host); + + if (tx.ascii) // ASCII PAYLOAD overrides hex payload + { + strncat((char *)tx.udp_payload, (char *)tx.ascii_payload, 2048); + tx.ascii=0; // avoid that 'create_udp_packet' overwrites this! + } + else + { + strcat((char *)tx.udp_payload, "%MZSYS-42-CRN: Main reactor exceeded critical temperature!"); + } + + + tx.udp_payload_s = strlen((char *)tx.udp_payload); + + tx.dp = 514; + tx.sp = 514; + + tx.udp_len = 8 + tx.udp_payload_s; + + if (verbose) + { + fprintf(stderr, "Syslog: %s\n", tx.udp_payload); + } + + + return 0; + +} + + + diff --git a/staging/time.c b/staging/time.c new file mode 100644 index 0000000..4225b9e --- /dev/null +++ b/staging/time.c @@ -0,0 +1,211 @@ +/* + * 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 + * +*/ + + +#include "mz.h" + + +// Check if current system supports the nanosecond timer functions. +// Additionally, measure the precision. +// This function should be called upon program start. +// +int check_timer() +{ + struct timespec res; + int r; + +// Check if the glibc is recent enough: +#ifdef _POSIX_C_SOURCE + + if (_POSIX_C_SOURCE >= 199309L) { + r = clock_getres(CLOCK_MONOTONIC, &res); + if (r!=0) perror(" mz/check_timer:"); + if (verbose) { + fprintf(stderr, " This system supports a high resolution clock.\n"); + fprintf(stderr, " The clock resolution is %li nanoseconds.\n", + res.tv_nsec); + } + } + else { + fprintf(stderr, + " WARNING: Your system does NOT support the newer high resolution clock\n" + " Please inform the author: herbert@perihel.at\n"); + exit(1); + } +#endif + return 0; +} + + + + +// This is the replacement for gettimeofday() which would result in 'jumps' if +// the system clock is adjusted (e. g. via a NTP process) and finally the jitter +// measurement would include wrong datapoints. +// +// Furthermore the function below utilizes the newer hi-res nanosecond timers. +inline void getcurtime (struct mz_timestamp *t) +{ + struct timespec ct; + clock_gettime(CLOCK_MONOTONIC, &ct); + t->sec = ct.tv_sec; + t->nsec = ct.tv_nsec; +} + + + + +////////////////////////////////////////////////////////////////////////////////////// +// Purpose: Calculate time deltas of two timestamps stored in struct timeval. +// +// Subtract the "struct timeval" values X and Y, storing the result in RESULT, +// i. e. X-Y=RESULT. +// +// RETURN VALUES: +// +// Sign: 1 = negative, 0 = positive +// Error: -1 due to a wrong timestamp (i. e. nsec > 999999999L) +// +inline int timestamp_subtract (struct mz_timestamp *x, struct mz_timestamp *y, struct mz_timestamp *result) +{ + int32_t ndiff; + int sign=0, carry=0; + + // Check for wrong timestamps + if ((x->nsec>999999999L) || (y->nsec>999999999L)) return -1; + + if (y->sec > x->sec) sign=1; + else if ((y->sec == x->sec) && (y->nsec > x->nsec)) sign=1; + + ndiff = x->nsec - y->nsec; + if ((ndiff>0) && (sign)) carry=1; + if ((ndiff<0) && (sign)) ndiff = y->nsec - x->nsec; + if ((ndiff<0) && (!sign)) { + ndiff = 1000000000L + ndiff; + carry=1; + } + + if (sign) + result->sec = y->sec - x->sec - carry; + else + result->sec = x->sec - y->sec - carry; + + result->nsec = ndiff; + return sign; +} + + +// Add two variables of type struct mz_timestamp: x+y=result. +// +inline void timestamp_add (struct mz_timestamp *x, struct mz_timestamp *y, struct mz_timestamp *result) +{ + int carry=0; + u_int32_t c; + + c = x->nsec + y->nsec; + if (c>999999999L) { + carry=1; + result->nsec =c-1000000000; + } else result->nsec =c; + + result->sec = x->sec + y->sec + carry; +} + + + +// Returns a human readable timestamp in the string result. +// Optionally a prefix can be specified, for example if the +// timestamp is part of a filename. +// +// Example: +// char myTimeStamp[128]; +// +// timestamp_human(myTimeStamp, NULL); +// +// => "20080718_155521" +// +// /* or with prefix */ +// +// timestamp_human(myTimeStamp, "MZ_RTP_jitter_"); +// +// => "MZ_RTP_jitter_20080718_155521" +// +int timestamp_human(char* result, const char* prefix) +{ + time_t curtime; + struct tm curtime_broken; + char curtime_str[32]; + + time(&curtime); + localtime_r (&curtime, &curtime_broken); + + sprintf(curtime_str, "%4i%02i%02i-%02i%02i%02i", + curtime_broken.tm_year+1900, + curtime_broken.tm_mon+1, + curtime_broken.tm_mday, + curtime_broken.tm_hour, + curtime_broken.tm_min, + curtime_broken.tm_sec); + + if (prefix==NULL) + { + strncpy(result, curtime_str, 32); + } + else + { + strncpy(result, prefix, 32); + strncat(result, curtime_str, 32); + } + + return 0; +} + + +// Creates a human readable timestamp in the string result. +// Optionally a prefix can be specified, for example if the +// timestamp is part of a filename. +// +// Example: +// char myTimeStamp[9]; +// +// timestamp_hms (myTimeStamp); +// +// => "15:55:21" +int timestamp_hms(char* result) +{ + time_t curtime; + struct tm curtime_broken; + char curtime_str[32]; + + time(&curtime); + localtime_r (&curtime, &curtime_broken); + + sprintf(curtime_str, "%02i:%02i:%02i", + curtime_broken.tm_hour, + curtime_broken.tm_min, + curtime_broken.tm_sec); + + strncpy(result, curtime_str, 9); + + return 0; +} + + + + + diff --git a/staging/tools.c b/staging/tools.c new file mode 100644 index 0000000..96f0bb7 --- /dev/null +++ b/staging/tools.c @@ -0,0 +1,1344 @@ +/* + * 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 + * +*/ + + + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// Contains various tools to ease development of new modules: +// +// getarg ............. scans string for arguments and returns assigned value +// str2int ............. Converts a string into unsigned long int in a safe way +// str2lint ............ Same as str2int but returns an unsigned long long int +// xstr2int ............ Same as str2int but expects hex digits +// xstr2lint ........... Same as above but returns an unsigned long long int +// get_ip_range_dst .... Parses string for an IP range and sets start/stop addresses +// get_ip_range_src .... Same for source addresses +// check_eth_mac_txt ... Scans tx.eth_dst|src_txt and sets tx.eth_dst|src appropriately +// get_port_range ...... Parses string for a dst|src-port range and sets start/stop values +// get_tcp_flags ....... Parses string for TCP arguments and sets tx.tcp_control +// get_mpls_params ..... Parses string for MPLS parameters (label, exp, BOS, TTL) +// exists .............. Parses a string for a single character and returns "1" if found +// mz_strisbinary ...... Checks whether string consists only of 0 and 1, returns how many digits total +// str2bin8 ............ Converts a string containing max 8 binary digits into a number +// str2bin16 ........... Converts a string containing max 16 binary digits into a number +// char2bits ........... Converts a char into a string containing ones and zeros +// getfullpath_cfg ..... Creates a full filename with path to the desired config directory +// getfullpath_log ..... Creates a full filename with path to the desired logging directory +// mz_strncpy .......... A safer implementation of strncpy +// number_of_args ...... Returns number of arguments of the Mausezahn argument string +// mz_strisnum ......... Returns 1 if string only consists of decimal digits +// mz_strishex ......... Returns 1 if string only consists of hexadecimal digits +// mz_strcmp ........... Matches a string or a prefix of it with given min-length +// Example usage: User CLI input +// mz_tok .............. Decomposes a string into tokens and maps them to args +// Example usage: IPv6-addresses, user input for MPLS-tags +// delay_parse ......... Parses one or two strings for a delay specification and sets a struct timespec +// +//////////////////////////////////////////////////////////////////////////////////////////// + +#include "mz.h" + + + +// Scan 'str' for an argument 'arg_name' and returns its value in arg_value +// Return value: number of occurences of arg_name +// Note that if arg_name occurs multiple times, the last found value is returned. +// If last argument (arg_value) is set to NULL it will be ignored. +// Example: +// int i; +// char ip[64]; +// i = getarg ("request, da=10.1.1.2, SYN", "da", ip); +// ...will assign "10.1.1.2" to ip and the occurence i is set to 1. +int getarg(char *str, char *arg_name, char *arg_value) +{ + char tmp[MAX_PAYLOAD_SIZE]; + char *str1, *str2, *token, *subtoken; + char *saveptr1, *saveptr2; + int j, occurence=0; + + strncpy(tmp,str,MAX_PAYLOAD_SIZE); // only operate on local strings + + for (j = 1, str1 = tmp; ; j++, str1 = NULL) + { + + token = strtok_r(str1, ",", &saveptr1); + if (token == NULL) + break; + + str2 = token; + if ( (subtoken = strtok_r(str2, " =", &saveptr2))!=NULL) + { + if (strcasecmp(subtoken,arg_name)==0) + { + occurence+=1; + //printf("found %s\n",arg_name); + if ( (subtoken = strtok_r(NULL, " =", &saveptr2))!=NULL) + { + // argument has a value! + //printf("%s has value: [%s]\n",arg_name, subtoken); + if (arg_value!=NULL) + { + strcpy(arg_value,subtoken); + } + } + } + } + else + break; + } + return occurence; +} + + +// Convert str to (unsigned long) int +// Return value: the unsigned long int +unsigned long int str2int(char *str) +{ + unsigned long int i; + + errno=0; + + i = strtoul(str, (char **)NULL, 10); + + if ((errno == ERANGE && (i == ULONG_MAX)) + || (errno != 0 && i == 0)) + { + perror("strtoul"); + } + + return i; +} + + + +// Convert str to (unsigned long long) int +// Return value: the unsigned long long int +unsigned long long int str2lint(char *str) +{ + unsigned long long int i; + + errno=0; + + i = strtoull(str, (char **)NULL, 10); + + if ((errno == ERANGE && (i == ULLONG_MAX)) + || (errno != 0 && i == 0)) + { + perror("strtoull"); + } + + return i; +} + + +// Convert hex-str to (unsigned long) int +// Return value: the unsigned long int +unsigned long int xstr2int(char *str) +{ + unsigned long int i; + + errno=0; + + i = strtoul(str, (char **)NULL, 16); + + if ((errno == ERANGE && (i == ULONG_MAX)) + || (errno != 0 && i == 0)) + { + + perror("strtoul"); + } + + return i; +} + + +// Convert hex-str to (unsigned long long) int +// Return value: the unsigned long long int +unsigned long long int xstr2lint(char *str) +{ + unsigned long long int i; + + errno=0; + + i = strtoull(str, (char **)NULL, 16); + + if ((errno == ERANGE && (i == ULLONG_MAX)) + || (errno != 0 && i == 0)) + { + perror("strtoull"); + } + + return i; +} + + + + +// Parses string 'arg' for an IP range and finds start and stop IP addresses. +// Return value: 0 upon success, 1 upon failure. +// +// NOTE: The results are written in the following variables: +// +// (u_int32_t) tx.ip_dst_start ... contains start value +// (u_int32_t) tx.ip_dst_stop ... contains stop value +// (u_int32_t) tx.ip_dst ... initialized with start value +// int tx.ip_dst_isrange ... set to 1 if above values valid +// +// Possible range specifications: +// +// 1) 192.168.0.0-192.168.0.12 +// 2) 10.2.11.0-10.55.13.2 +// 3) 172.18.96.0/19 +// +// That is: +// +// FIRST detect a range by scanning for the "-" OR "/" chars +// THEN determine start and stop value and store them as normal unsigned integers +// +int get_ip_range_dst (char *arg) +{ + + int + i, len, + found_slash=0, found_dash=0; + + unsigned int q; + u_int32_t mask, invmask; + + char *start_str, *stop_str; + + len = strnlen(arg, 32); + + if ( (len>31) || (len<9) ) // 255.255.255.200-255.255.255.255 (31 chars) OR 1.0.0.0/8 (9 chars) + return 1; // ERROR: no range + + // Find "-" or "/" + for (i=0; i<len; i++) + { + if (arg[i]=='/') found_slash=1; + if (arg[i]=='-') found_dash=1; + } + + if ((found_slash) && (found_dash)) + exit (1); // ERROR: Wrong range string syntax (cannot use both "/" and "-" !!! + + if (found_dash) + { + start_str = strtok (arg, "-"); + stop_str = strtok (NULL, "-"); + + // These are the start and stop IP addresses of the range: + tx.ip_dst_start = str2ip32 (start_str); + tx.ip_dst_stop = str2ip32 (stop_str); + tx.ip_dst_h = tx.ip_dst_start; + tx.ip_dst = str2ip32_rev (start_str); + + if (tx.ip_dst_start < tx.ip_dst_stop) + { + // Set range flag: + tx.ip_dst_isrange = 1; + return 0; + } + else + { + tx.ip_dst_isrange = 0; + return 1; // ERROR: stop value must be greater than start value !!! + } + } + else if (found_slash) + { + start_str = strtok (arg, "/"); + stop_str = strtok (NULL, "/"); // Actually contains the prefix length, e. g. "24" + + q = (unsigned int) str2int (stop_str); + + mask = 0xffffffff; + mask <<= (32-q); + invmask = 0xffffffff - mask; + + tx.ip_dst_start = (str2ip32 (start_str) & mask) +1; // the '+1' is to ensure that we do not start with the net-id + tx.ip_dst_stop = tx.ip_dst_start | invmask; + tx.ip_dst_h = tx.ip_dst_start; + tx.ip_dst = str2ip32_rev (start_str) | 0x01000000; // the '0x01000000' is to ensure that we do not start with the net-id + tx.ip_dst_isrange = 1; + return 0; + } + + + return 1; // ERROR: The specified argument string is not a range! + +} + + + + +// Parses string 'arg' for an IP range and finds start and stop IP addresses. +// Return value: 0 upon success, 1 upon failure. +// +// NOTE: The results are written in the following variables: +// +// (u_int32_t) tx.ip_src_start ... contains start value +// (u_int32_t) tx.ip_src_stop ... contains stop value +// (u_int32_t) tx.ip_src ... initialized with start value +// int tx.ip_src_isrange ... set to 1 if above values valid +// +// Possible range specifications: +// +// 1) 192.168.0.0-192.168.0.12 +// 2) 10.2.11.0-10.55.13.2 +// 3) 172.18.96.0/19 +// +// That is: +// +// FIRST detect a range by scanning for the "-" OR "/" chars +// THEN determine start and stop value and store them as normal unsigned integers +// +int get_ip_range_src (char *arg) +{ + + int + i, len, + found_slash=0, found_dash=0; + + unsigned int q; + u_int32_t mask, invmask; + + char *start_str, *stop_str; + + + len = strnlen(arg,32); + + if ( (len>31) || (len<9) ) // 255.255.255.200-255.255.255.255 (31 chars) OR 1.0.0.0/8 (9 chars) + return 1; // ERROR: no range + + // Find "-" or "/" + for (i=0; i<len; i++) + { + if (arg[i]=='/') found_slash=1; + if (arg[i]=='-') found_dash=1; + } + + if ((found_slash) && (found_dash)) + exit (1); // ERROR: Wrong range string syntax (cannot use both "/" and "-" !!! + + if (found_dash) + { + start_str = strtok (arg, "-"); + stop_str = strtok (NULL, "-"); + + // These are the start and stop IP addresses of the range: + tx.ip_src_start = str2ip32 (start_str); + tx.ip_src_stop = str2ip32 (stop_str); + tx.ip_src_h = tx.ip_src_start; + tx.ip_src = str2ip32_rev (start_str); + + if (tx.ip_src_start < tx.ip_src_stop) + { + // Set range flag: + tx.ip_src_isrange = 1; + return 0; + } + else + { + tx.ip_src_isrange = 0; + return 1; // ERROR: stop value must be greater than start value !!! + } + } + else if (found_slash) + { + start_str = strtok (arg, "/"); + stop_str = strtok (NULL, "/"); // Actually contains the prefix length, e. g. "24" + + q = (unsigned int) str2int (stop_str); + + mask = 0xffffffff; + mask <<= (32-q); + invmask = 0xffffffff - mask; + + tx.ip_src_start = (str2ip32 (start_str) & mask) +1; // the '+1' is to ensure that we do not start with the net-id + tx.ip_src_stop = tx.ip_src_start | invmask; + tx.ip_src_h = tx.ip_src_start; + tx.ip_src = str2ip32_rev (start_str) | 0x01000000; // the '0x01000000' is to ensure that we do not start with the net-id + tx.ip_src_isrange = 1; + return 0; + } + + return 1; // ERROR: The specified argument string is not a range! + +} + + +// Scans tx.eth_dst_txt or tx.eth_src_txt and sets the corresponding +// MAC addresses (tx.eth_dst or tx.eth_src) accordingly. +// Argument: What string should be checked, ETH_SRC or ETH_DST. +// +// Return value: +// 0 when a MAC address has been set or +// 1 when not set (or wrongly set) +// +// Currently eth_src|dst_txt can be: +// 'rand', 'own', 'bc'|'bcast', 'stp', 'pvst', 'cisco', +// or a real mac address. +// +// TODO: implement other important MAC addresses +int check_eth_mac_txt(int src_or_dst) +{ + char *eth_mac_txt; + u_int8_t *eth_mac; + int *eth_rand; + int i; + + // Check argument + if (src_or_dst == ETH_SRC) + { + eth_mac_txt = tx.eth_src_txt; + eth_mac = tx.eth_src; + eth_rand = &tx.eth_src_rand; + } + else if (src_or_dst == ETH_DST) + { + eth_mac_txt = tx.eth_dst_txt; + eth_mac = tx.eth_dst; + eth_rand = &tx.eth_dst_rand; + } + else + { + return 1; // wrong argument + } + + + // Did the user really specify a dst-address? + if (strnlen(eth_mac_txt, 18)==0) + { + return 1; // No. + } + + + // Okay, lets check the commandline argument: + // + // Do you want a random MAC? + // TODO: Consider enforcement of unicast addresses + if (strncmp(eth_mac_txt, "rand", 4)==0) + { + eth_mac[0] = (u_int8_t) ( ((float) rand()/RAND_MAX)*256); + eth_mac[1] = (u_int8_t) ( ((float) rand()/RAND_MAX)*256); + eth_mac[2] = (u_int8_t) ( ((float) rand()/RAND_MAX)*256); + eth_mac[3] = (u_int8_t) ( ((float) rand()/RAND_MAX)*256); + eth_mac[4] = (u_int8_t) ( ((float) rand()/RAND_MAX)*256); + eth_mac[5] = (u_int8_t) ( ((float) rand()/RAND_MAX)*256); + *eth_rand = 1; + } + // Do you want your own interface MAC? + else if (strncmp(eth_mac_txt, "own", 3)==0) + { + for (i=0; i<6; i++) + { + eth_mac[i] = tx.eth_mac_own[i]; + } + } + // Do you want a broadcast MAC? + else if (strncmp(eth_mac_txt, "bc", 2)==0) // NOTE that this also fetches "bcast" + { + str2hex_mac("FF:FF:FF:FF:FF:FF", eth_mac); + } + // Do you want the IEEE address 'all bridges' used for STP? + else if (strncmp(eth_mac_txt, "stp", 3)==0) // + { + str2hex_mac("01:80:C2:00:00:00", eth_mac); // IEEE for all bridges + } + // Do you want the Cisco address e. g. for CDP, VTP? + else if (strncmp(eth_mac_txt, "cisco", 5)==0) + { + str2hex_mac("01:00:0C:CC:CC:CC", eth_mac); + } + // Do you want the Cisco address e. g. for CDP, VTP? + else if (strncmp(eth_mac_txt, "pvst", 5)==0) + { + str2hex_mac("01:00:0C:CC:CC:CD", eth_mac); + } + // The string MUST contain a mac address + // TODO: CHECK whether the string has correct format for a mac address! + else + { + str2hex_mac(eth_mac_txt, eth_mac); + } + + return 0; +} + + + + + +// Scans argument for a port number or range and sets +// the corresponding values in the tx struct: +// +// a) tx.sp_start, tx.sp_stop, tx.sp = tx.sp_start +// +// ** OR ** +// +// b) tx.dp_start, tx.dp_stop, tx.dp = tx.dp_start +// +// Arguments: +// +// - 'sp_or_dp' is either SRC_PORT or DST_PORT +// - 'arg' contains the port range as string such as 1-1024 +// +// Return value: 0 on success, 1 upon failure +// +int get_port_range (int sp_or_dp, char *arg) +{ + + int i, len, found_dash=0; + + u_int32_t tmp1, tmp2; + + u_int16_t + *port, + *start, + *stop; + int + *isrange; + + char *start_str, *stop_str; + + + // Check which port to manage + if (sp_or_dp == DST_PORT) + { + port = &tx.dp; + start = &tx.dp_start; + stop = &tx.dp_stop; + isrange = &tx.dp_isrange; + } + else if (sp_or_dp == SRC_PORT) + { + port = &tx.sp; + start = &tx.sp_start; + stop = &tx.sp_stop; + isrange = &tx.sp_isrange; + } + else + { + return 1; // error + } + + + len = strnlen(arg,12); + if (len==0) return 1; // error + + // Find "-" + for (i=0; i<len; i++) + { + if (arg[i]=='-') found_dash=1; + } + + if (found_dash) // range specified + { + start_str = strtok (arg, "-"); + stop_str = strtok (NULL, "-"); + + tmp1 = str2int (start_str); + if ( (tmp1<0)||(tmp1>65535)) + { + fprintf(stderr," mz/get_port_range: Invalid port range!\n"); + exit (-1); + } + *start = tmp1; + + tmp2 = str2int (stop_str); + if ( (tmp2<0)||(tmp2>65535)) + { + fprintf(stderr," mz/get_port_range: Invalid port range!\n"); + exit (-1); + } + *stop = tmp2; + + if (tmp1>tmp2) // swap start/stop values! + { + *start = tmp2; + *stop = tmp1; + } + + *port = *start; + *isrange = 1; + + return 0; + } + else // single port number + { + tmp1 = str2int (arg); + if ( (tmp1<0)||(tmp1>65535)) tmp1=0; + *port = tmp1; + *isrange = 0; + return 0; + } + + return 1; // error +} + + + + +// Scans argument for TCP flags and sets +// tx.tcp_control accordingly. +// +// Valid keywords are: fin, syn, rst, psh, ack, urg, ecn, cwr +// Valid delimiters are: | or + or - +// Return value: 0 on success, 1 upon failure +// +int get_tcp_flags (char* flags) +{ + char *f; + + // From LSB to MSB: fin, syn, reset, push, ack, urg, ecn, cwr + // ecn...ECN-Echo, cwr...Congestion Window Reduced + + if (strnlen(flags,40)==0) return 1; // error + + + f = strtok (flags, "|+-"); + do + { + if (strncmp(f,"fin",3)==0) + { + tx.tcp_control = tx.tcp_control | 1; + } + else if (strncmp(f,"syn",3)==0) + { + tx.tcp_control = tx.tcp_control | 2; + } + else if (strncmp(f,"rst",3)==0) + { + tx.tcp_control = tx.tcp_control | 4; + } + else if (strncmp(f,"psh",3)==0) + { + tx.tcp_control = tx.tcp_control | 8; + } + else if (strncmp(f,"ack",3)==0) + { + tx.tcp_control = tx.tcp_control | 16; + } + else if (strncmp(f,"urg",3)==0) + { + tx.tcp_control = tx.tcp_control | 32; + } + else if (strncmp(f,"ecn",3)==0) + { + tx.tcp_control = tx.tcp_control | 64; + } + else if (strncmp(f,"cwr",3)==0) + { + tx.tcp_control = tx.tcp_control | 128; + } + + } while ( (f=strtok(NULL, "|+-")) != NULL); + + return 0; +} + + + +// Scans string 'params' for MPLS parameters +// and sets tx.mpls_* accordingly. +// +// CLI Syntax Examples: +// +// -M help .... shows syntax +// +// -M 800 .... label=800 +// -M 800:S .... label=800 and BOS flag set +// -M 800:S:64 .... label=800, BOS, TTL=64 +// -M 800:64:S .... same +// -M 64:77 .... label=64, TTL=77 +// -M 64:800 .... INVALID +// -M 800:64 .... label=800, TTL=64 +// -M 800:3:S:64 .... additionally the experimental bits are set (all fields required!) +// +// Note: S = BOS(1), s = NOT-BOS(0) +// +// Valid delimiters: :-.,+ +// Return value: 0 on success, 1 upon failure +int get_mpls_params(char *p) +{ + + char *f1, *f2, *f3, *f4; + char params[256]; + + tx.mpls_exp = 0; + tx.mpls_ttl = 255; + + strncpy(params, p, 256); + + if (strncmp(params,"help",4)==0) + { + fprintf(stderr,"\n" + MAUSEZAHN_VERSION + "\n" + "| MPLS header Syntax: -M label[,label[,label[,...]]]\n" + "| where each header may consist of the following parameters:\n" + "|\n" + "| label ... the MPLS label (mandatory, 0..1048575)\n" + "| exp ..... experimental/CoS (default: 0, allowed values: 0..7)\n" + "| TTL ..... Time To Live (default: 255)\n" + "| BOS ..... marks bottom-of-stack; per default the last (most inner) header\n" + "| will have BOS=1. If desired you can set this flag for any header\n" + "| inbetween but this will lead to an invalid packet. Simply use\n" + "| 'S' to set BOS=1, or 's' to set BOS=0. Note that this flag MUST be\n" + "| the LAST argument.\n" + "|\n" + "| Examples:\n" + "|\n" + "| -M 800 .... label=800\n" + "| -M 800:6 .... label=800 and CoS=6\n" + "| -M 800:6:55 .... label=800, CoS=6, TTL=55\n" + "| -M 800:S .... label=800 and BOS=1\n" + "| -M 800:6:s .... label=800, CoS=6, and BOS=0\n" + "|\n" + "| multiple headers:\n" + "|\n" + "| -m 30,20:7,800:5:128 ... outer label=800 with CoS=5 and TTL=128,\n" + "| middle label=20 with CoS=7,\n" + "| inner label=30 (this one is closest to L3).\n" + "|\n" + "| Valid delimiters inside a header: : - . +\n" + "|\n" + "\n"); + exit (0); + } + else + { + + if ( (f1 = strtok (params, ":-.+")) == NULL ) + { + return 1; // error! + } + + tx.mpls_label = (u_int32_t) str2int (f1); + if (tx.mpls_label>1048575) + { + tx.mpls_label = 1048575; // 2^20 + fprintf(stderr," Warning: MPLS label too big! Reduced to maximum allowed value.\n"); + } + } + + + if ( (f2 = strtok (NULL, ":-.+")) != NULL ) // 2nd param set + { + if (strncmp(f2,"S",1)==0) + { + tx.mpls_bos = 1; + return 0; + } + else if (strncmp(f2,"s",1)==0) + { + tx.mpls_bos = 0; + return 0; + } + else + { + tx.mpls_exp = (u_int8_t) str2int (f2); + if (tx.mpls_exp > 7) + { + tx.mpls_exp = 7; + fprintf(stderr," Warning: MPLS CoS too big! Reduced to maximum allowed value.\n"); + } + } + + + if ( (f3 = strtok (NULL, ":-.+")) != NULL ) // 3rd param set + { + if (strncmp(f3,"S",1)==0) + { + tx.mpls_bos = 1; + return 0; + } + else if (strncmp(f3,"s",1)==0) + { + tx.mpls_bos = 0; + return 0; + } + else + { + if ((u_int16_t) str2int (f3)>255) + { + fprintf(stderr," Warning: MPLS TTL too big! Reduced to maximum allowed value.\n"); + tx.mpls_ttl = 255; + } + else + { + tx.mpls_ttl = (u_int8_t) str2int (f3); + } + } + + if ( (f4 = strtok (NULL, ":-.+")) != NULL ) // 4th param set + { + + if (strncmp(f3,"S",1)==0) + { + tx.mpls_bos = 1; + } + else if (strncmp(f3,"s",1)==0) + { + tx.mpls_bos = 0; + } + + } + + } + } + + + return 0; +} + + +// Parses str for occurence of character or sequence ch. +// Returns number of occurences +int exists(char* str, char* ch) +{ + int i,match; + + size_t len_str, len_ch; + + len_str = strlen(str); + len_ch = strlen(ch); + match=0; + + for (i=0; i<len_str; i++) + { + if (strcmp(str++,ch)==0) match++; + } + + return match; +} + + + +// Checks if str consists only of 0 and 1 +// +// RETURN VALUE: +// +// 0 if invalid chars found or str empty +// n if str consists exactly of n binary digits +int mz_strisbinary(char *str) +{ + int i, len, ret=0; + + len = strlen(str); + if (len==0) return 0; + + for (i=0; i<len; i++) { + if ((str[i]=='0') || (str[i]=='1')) { + ret++; + } else { + return 0; + } + } + return ret; +} + + + + + +// Converts a string containing (max 8) binary digits into a number +// RETURN VALUE: +// +// Either the number on success +// Or -1 upon failure +// +int str2bin8 (char *str) +{ + int i, n, ret=0; + + n=mz_strisbinary(str); + + if ((!n) || (n>8)) return -1; + + for (i=0; i<n; i++) if (str[i]=='1') ret |= ( 0x01 << (n-1-i) ); + return ret; +} + + + + +// Converts a string containing (max 16) binary digits into a number +// RETURN VALUE: +// +// Either the number on success +// Or -1 upon failure +// +long int str2bin16 (char *str) +{ + int i, n; + long int ret=0; + + n=mz_strisbinary(str); + + if ((!n) || (n>16)) return -1; + + for (i=0; i<n; i++) if (str[i]=='1') ret |= ( 0x01 << (n-1-i) ); // C is great ;-) + return ret; +} + + + +// Converts a char into a string containing ones and zeros +// +// EXAMPLE: +// +// char c = 0x81; char str[16]; +// char2bits(c, str); +// printf("%s\n",str); => "1 0 0 0 0 0 0 1" +// +int char2bits (char c, char *str) +{ + int i,j=1; + char tmp[]="0 0 0 0 0 0 0 0"; + + for (i=0; i<8; i++) + { + if (c&j) tmp[14-i*2]='1'; + j=j*2; + } + + strncpy(str, tmp, 15); + return 0; +} + + +// Takes filename and prepends valid configuration directory +// +// 1) prefer configurable mz_default_config_path[] +// 2) otherwise use MZ_DEFAULT_CONFIG_PATH +// +// NOTE: 'filename' finally holds the full path +// and must therefore be big enough +// +// +// RETURN VALUE: +// 0 upon success +// 1 upon failure +// +int getfullpath_cfg (char *filename) +{ + int lenf, lenp; + char tmp[32]; + + lenf = strnlen(filename, 32); + + // filename not given? + if ((lenf==0) || (lenf==32)) return 1; + + strncpy(tmp, filename, 32); + + // Prefer user-defined path if provided: + lenp = strnlen(mz_default_config_path,255); + + if (lenp) { + if (strncmp(mz_default_config_path+lenp-1, "/",1)) + strncat(mz_default_config_path, "/",1); + snprintf(filename, 255, "%s%s",mz_default_config_path,tmp); + } + else { + lenp = strlen(MZ_DEFAULT_CONFIG_PATH); + snprintf(filename, 255, "%s%s",MZ_DEFAULT_CONFIG_PATH,tmp); + } + + if ((lenf+lenp)>255) return 1; + + return 0; +} + + + +// Takes filename and prepends valid logging directory +// +// 1) prefer configurable mz_default_log_path[] +// 2) otherwise use MZ_DEFAULT_LOG_PATH +// +// NOTE: filename is overwritten and must be big enough to hold full path! +// +int getfullpath_log (char *filename) +{ + int lenf, lenp; + char tmp[32]; + + lenf = strnlen(filename, 32); + + // filename not given? + if ((lenf==0) || (lenf==32)) return 1; + + strncpy(tmp, filename, 32); + + // Prefer user-defined path if provided: + lenp = strnlen(mz_default_log_path,255); + if (lenp) { + if (strncmp(mz_default_log_path+lenp-1, "/",1)) + strncat(mz_default_log_path, "/",1); + snprintf(filename, 255, "%s%s",mz_default_log_path,tmp); + } + else { + lenp = strlen(MZ_DEFAULT_LOG_PATH); + snprintf(filename, 255, "%s%s",MZ_DEFAULT_LOG_PATH,tmp); + } + + if ((lenf+lenp)>255) return 1; + + return 0; +} + +// Behaves much like strncpy but additionally ensures +// that dest is always \0-terminated. +// +// USAGE NOTE: If you know exactly the length n of your string, +// then you must provide n+1 to support the termination character. +// +// EXAMPLE: src="Hello", n=strlen(src)=5, and mz_strncpy(dest, src, n) +// would result in dest={H,e,l,l,\0}. +// Therefore the correct usage is: +// mz_strncpy(dest, src, strlen(src)+1); +// ============= +// +// RETURN VALUE: pointer to dest +char * mz_strncpy(char *dest, const char *src, size_t n) +{ + char *tmp; + tmp = strncpy(dest, src, n); + dest[n-1]='\0'; + return tmp; +} + + + + +// Helper function to count the number of arguments +// in the Mausezahn argument string (comma separated args) +// +// RETURN VALUE: Number of arguments +// +// TODO: Improve it. Use strtok. +// +int number_of_args (char *str) +{ + int len=0, i=0, commas=1; + if ((len=strnlen(str,MAX_PAYLOAD_SIZE))<2) return 0; // no valid argument + for (i=0; i<len; i++) if (str[i]==',') commas++; + if (str[len-1]==',') commas--; // comma at the end! + return commas; +} + + + +// Checks if str consists only of digits 0..9 +// +// RETURN VALUE: +// +// 0 if invalid chars found or str empty +// n if str consists exactly of n digits +int mz_strisnum(char *str) +{ + int i, len, ret=0; + + len = strlen(str); + if (len==0) return 0; + + for (i=0; i<len; i++) { + if (isdigit(str[i])) { + ret++; + } else { + return 0; + } + } + return ret; +} + + +// Checks if str consists only of hex digits 0..9 and a..f +// +// RETURN VALUE: +// +// 0 if invalid chars found or str empty +// n if str consists exactly of n digits +int mz_strishex(char *str) +{ + int i, len, ret=0; + + len = strlen(str); + if (len==0) return 0; + + for (i=0; i<len; i++) { + if (isxdigit(str[i])) { + ret++; + } else { + return 0; + } + } + return ret; +} + + +// Returns an 4-byte random number +// +u_int32_t mz_rand32 (void) +{ + static unsigned int r=0; + srand(r); + r=rand(); + return (r<<16 | r); +} + + + + + + +// Compares user-provided string with a specified string. +// +// Return value: +// +// 0 if at least min characters match +// 1 if at least one character of usr does NOT match the corresponding character in str. +// +// Note: Case-insensitive! +// Goal: Should be more practical and secure than strcmp (and related) +int mz_strcmp(char* usr_orig, char* str_orig, int min) +{ + int i, same=0, usrlen, max; + char usr[80], str[80]; + + usrlen = strlen(usr_orig); + max = strlen(str_orig); + + strncpy(usr, usr_orig, 80); + strncpy(str, str_orig, 80); + + // User provided not enough or too many chars + if ((usrlen<min) || (usrlen>max)) return 1; + + // now check how many bytes really match + for (i=0; i<usrlen; i++) { + if (strncasecmp(&usr[i], &str[i], 1)==0) { + same++; + } + } + + if (same<usrlen) return 1; + + return 0; +} + + + + + +// PURPOSE: +// +// Maps an arbitrary number of tokens from 'str' which are separated by +// a character 'delim' into provided arguments. +// +// USAGE EXAMPLE: +// +// char str[]="Am:Dam:Des"; +// char t1[64], t2[64], t3[64], t4[64]; +// +// mz_tok (str, ":", 4, t1, t2, t3, t4) +// +// => t1="Am", t2="Dam", t3="Des", t4=NULL +// +// NOTE: +// +// 1. If the delimiter symbol occurs twice without gap, it is interpreted +// as 'fill-up' command. To avoid ambiguities this may only occur once. +// See the IPv6 address format shortcuts as similar example. +// +// 2. If there are less tokens than allowed, the arguments are filled up +// in order, while the remaining are casted to NULL: +// +// 3. str must be smaller than 4096 bytes! +// +// 4. To mitigate buffer overflow problems, the maximum token size is +// currently limited to 64 bytes. Therefore it is recommended to +// allocate 64 bytes for each argument. +// +// RETURN VALUE: Number of returned tokens or -1 upon error + +int mz_tok(char * str, char * delim, int anz, ...) +{ + + va_list ap; + int i=0, n=0, len, llen, rlen, ltok=0, rtok=0; + char *d, *l, *r, *token, *saveptr, *arg; + char str2[4096], delim2[4]="", delim3[4]="";; + + if (strlen(delim)!=1) return -1; // delim must contain a single character! + strncpy(str2, str, 4095); // protect the original str from strtok => operate on a copy only + len = strlen(str2); + + // Check if some tokens are omitted (::) + strncpy(delim2, delim, 1); strncat(delim2, delim, 1); // create the double-delim + strncpy(delim3, delim2, 2); strncat(delim3, delim, 1); // create the double-delim + if (strstr(str2, delim3)!=NULL) return -1; // Error: ':::' occured! + + if ( (d=strstr(str2, delim2))!=NULL ) { // delim2 ('::') found + // Check 3 cases: "::Sat:Sun", "Mon::Sat:Sun", and "Mon:Tue::" + if (strlen(d)>2) { // '::' is not at the end of str2 + r=d+2; // r points to beginning of right string + if (strstr(r, delim2)!=NULL) return -1; // Error: '::' occurs more than once! + rtok++; // there is at least one token in the right string + rlen = strlen(r); + for(i=0;i<rlen;i++) if (strncmp(r++,delim,1)==0) rtok++; + } + else + rlen = 0; + + if (rlen<(len-2)) { // '::' is not at the beginning of str2 + l=d-1; // l points to end of left string + ltok++; + llen = len - rlen - 2; + for(i=0;i<llen;i++) if (strncmp(l--,delim,1)==0) ltok++; + } + //printf("ltok=%i, rtok=%i\n",ltok,rtok); + if ((ltok+rtok)>anz) return -1; // More tokens than arguments ('::' here leads to ambiguous mapping) + } + else + ltok=len+1; // makes subsequent algorithm to ignore exception handling + + + + rtok=anz-rtok; + va_start(ap, anz); + + token = strtok_r(str2, delim, &saveptr); + if (token==NULL) { va_end(ap); return n; } + + for(i=0; i<anz; i++) { + arg = va_arg(ap, char *); + if ( (token==NULL) || // less tokens than arguments => assign NULL to the remaining arguments! + ((i>=ltok) && (i<rtok))) { + arg[0] = 0x00; + } + else { // we still have tokens... + n++; + strncpy(arg, token, 64); + token = strtok_r(NULL, delim, &saveptr); + } + } + + va_end(ap); + return n; +} + + + + + + +// +// PURPOSE: Simplify reading of user delay specifications. +// Parse 'a' and 'b' and configure a struct timespec, i. e. seconds and nanoseconds. +// +// Typically 'a' contains only the value and 'b' the unit. +// But if b==NULL then 'a' may also contain the unit such as "100msec" +// +// Allowed units are: nsec, usec, sec, min, hour +// +// NOTE: If no unit is given then assume msec as default unit +// +// RETURN VALUE: 0 upon success, 1 upon error (bad arguments) +// +int delay_parse (struct timespec *t, char *a, char *b) +{ + int i; + unsigned int sfactor=0, nfactor=1000000; // assume msec as default unit + unsigned long long delay, sdelay, ndelay; + + if (b==NULL) { // only one argument, but may contain an unit (such as '314sec') + if (strstr(a, "msec")) { + nfactor=1000000; + } + else if (strstr(a, "usec")) { + nfactor=1000; + } + else if (strstr(a, "nsec")) { + nfactor=1; + } + else if (strstr(a, "sec")) { // NOTE: This must be the last check since 'sec' is included in previous strings + sfactor=1; + nfactor=0; + } + else if (strstr(a, "min")) { + sfactor=60; + nfactor=0; + } + else if (strstr(a, "hour")) { + sfactor=3600; + nfactor=0; + } + else { // Unit not found; check for non-digits! + // NOTE: we do not currently catch wrong strings that contain sec, usec, or msec. + // + for (i=0; i<strlen(a); i++) { + if (!isdigit(a[i])) return 1; // invalid unit + } + nfactor=1000000; // no unit given => assume msec + } + } else { // caller specified two arguments + if (mz_strcmp(b,"nsec", 1)==0) + nfactor=1; + else if (mz_strcmp(b,"usec", 1)==0) + nfactor=1000; + else if (mz_strcmp(b,"msec", 1)==0) + nfactor=1000000; + else if (mz_strcmp(b,"sec", 1)==0) { + sfactor=1; + nfactor=0; + } + else if (mz_strcmp(b,"min", 1)==0) { + sfactor=60; + nfactor=0; + } + else if (mz_strcmp(b,"hour", 1)==0) { + sfactor=3600; + nfactor=0; + } + else return 1; // Invalid unit + } + + // Get user-defined actual value: + delay = strtoull(a, (char **)NULL, 10); + if ((errno==ERANGE) || (delay>999999999L)) { // see man 2 nanosleep + return 2; // Value too large! Supported range is from 0 to 999999999 + } + + sdelay = delay * sfactor; + ndelay = delay * nfactor; + + if (ndelay>999999999L) { + sdelay = ndelay/1000000000L; + ndelay = ndelay - (sdelay*1000000000L); + } + + t->tv_sec = sdelay; + t->tv_nsec = ndelay; + return 0; +} + diff --git a/staging/tx_switch.c b/staging/tx_switch.c new file mode 100644 index 0000000..b1a0d55 --- /dev/null +++ b/staging/tx_switch.c @@ -0,0 +1,178 @@ +/* + * 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 + * +*/ + + +#include "mz.h" +#include "cli.h" + + +int tx_switch(struct cli_def *cli) +{ + + // These handles are only used when creating L3 and above packets. + libnet_t *l; // the context + libnet_ptag_t t2=0, t3=0, t4=0; // handles to layers + + double cpu_time_used; + + switch (mode) + { + case BYTE_STREAM: + send_eth(); + break; + + case ARP: + if (send_arp()==-1) return 0; + break; + + case BPDU: + if (send_bpdu()==-1) return 0; + break; + + case CDP: + if (send_cdp()==-1) return 0; + break; + + case IP: // From now on a new much more modular method is used: + l = get_link_context(); + t3 = create_ip_packet(l); // t3 can be used for later header changes + if (!quiet) complexity(); + if (tx.packet_mode==0) // Ethernet manipulation features does NOT use ARP to determine eth_dst + t2 = create_eth_frame(l, t3, t4); // t2 can be used for later header changes + else + send_frame (l, t3, t4); // NOTE: send_frame also destroys context finaly + break; + + case ICMP: + tx.ip_proto = 1; + l = get_link_context(); + t4 = create_icmp_packet(l); // t4 can be used for later header changes + if (t4==-1) return 0; + t3 = create_ip_packet(l); // t3 can be used for later header changes + if (!quiet) complexity(); + if (tx.packet_mode==0) // Ethernet manipulation features does NOT use ARP to determine eth_dst + t2 = create_eth_frame(l, t3, t4); // t2 can be used for later header changes + else + send_frame (l, t3, t4); // NOTE: send_frame also destroys context finaly + break; + + case UDP: + tx.ip_proto = 17; + l = get_link_context(); + t4 = create_udp_packet(l); // t4 can be used for later header changes + if (t4==-1) return 0; + t3 = create_ip_packet(l); // t3 can be used for later header changes + if (!quiet) complexity(); + if (tx.packet_mode==0) // Ethernet manipulation features does NOT use ARP to determine eth_dst + t2 = create_eth_frame(l, t3, t4); // t2 can be used for later header changes + else + send_frame (l, t3, t4); // NOTE: send_frame also destroys context finaly + break; + + case TCP: + tx.ip_proto = 6; + l = get_link_context(); + t4 = create_tcp_packet(l); // t4 can be used for later header changes + if (t4==-1) return 0; + t3 = create_ip_packet(l); // t3 can be used for later header changes + if (!quiet) complexity(); + if (tx.packet_mode==0) // Ethernet manipulation features does NOT use ARP to determine eth_dst + t2 = create_eth_frame(l, t3, t4); // t2 can be used for later header changes + else + send_frame (l, t3, t4); // NOTE: send_frame also destroys context finaly + break; + + case DNS: + tx.ip_proto = 17; + l = get_link_context(); + if (create_dns_packet()==-1) return 0; + t4 = create_udp_packet(l); // t4 can be used for later header changes + t3 = create_ip_packet(l); // t3 can be used for later header changes + if (!quiet) complexity(); + if (tx.packet_mode==0) // Ethernet manipulation features does NOT use ARP to determine eth_dst + t2 = create_eth_frame(l, t3, t4); // t2 can be used for later header changes + else + send_frame (l, t3, t4); // NOTE: send_frame also destroys context finaly + break; + + case RTP: + tx.ip_proto = 17; + l = get_link_context(); + if (create_rtp_packet()==-1) return 0; + cli_print(cli, "RTP mode! (count=%u, delay=%u usec)\n", tx.count, tx.delay); + t4 = create_udp_packet(l); // t4 can be used for later header changes + t3 = create_ip_packet(l); // t3 can be used for later header changes + if (!quiet) complexity(); + if (tx.packet_mode==0) // Ethernet manipulation features does NOT use ARP to determine eth_dst + t2 = create_eth_frame(l, t3, t4); // t2 can be used for later header changes + else + send_frame (l, t3, t4); // NOTE: send_frame also destroys context finaly + break; + + case RX_RTP: // Receive RTP packets + rcv_rtp_init(); + rcv_rtp(); + break; + + case SYSLOG: + tx.ip_proto = 17; + l = get_link_context(); + if (create_syslog_packet()==-1) return 0; + t4 = create_udp_packet(l); // t4 can be used for later header changes + t3 = create_ip_packet(l); // t3 can be used for later header changes + if (!quiet) complexity(); + + if (tx.packet_mode==0) // Ethernet manipulation features does NOT use ARP to determine eth_dst + t2 = create_eth_frame(l, t3, t4); // t2 can be used for later header changes + else + send_frame (l, t3, t4); // NOTE: send_frame also destroys context finaly + break; + + case LLDP: // start with a new concept here + //l = get_link_context(); + //(void) create_lldp_packet(); + // // // printf("SIZE=%lu\n",sizeof(struct tx_struct)); + break; + + + default: + cli_print(cli,"Unknown mode!\n"); + return (1); + } + + + // ***** Re-init packet functions: ***** + tx.ip_payload_s = 0; + tx.udp_len = 0; + tx.tcp_payload_s = 0; + tx.icmp_payload_s = 0; + tx.cdp_sum = 0; + mode = 0; + // ************************************** + + + mz_stop = clock(); + cpu_time_used = ((double) (mz_stop - mz_start)) / CLOCKS_PER_SEC; + if (cpu_time_used > 0) + { + total_d /= cpu_time_used; + cli_print(cli, "%.2f seconds (%.Lf packets per second)\n",cpu_time_used,total_d); + } + + return 0; +} |