/* DiffServ Backend Processing in Linux Platform - Nov. 6, 2000 - by jay@postech.ac.kr */ #include #include #include #include #include "utils.h" #include "tc_util.h" #include "tc_common.h" #include "diffServLinux.h" struct rtnl_handle rth; struct iftable *ifhead, *ifcur; struct classifiertable *clhead, *clcur; int classifier_count = 0; int sixtupleclfr_count = 0; int statistics_count = 0; int init_rtnl(void) { if (rtnl_open(&rth, 0) < 0) { fprintf(stderr, "Cannot open rtnetlink\n"); exit(1); } return 0; } int iftable_insert(int ix, char *name) { struct iftable *this; this = (struct iftable *)calloc(1, sizeof(struct iftable)); this->index = ix; strcpy(this->name,name); this->tcEnable = FALSE; this->next = NULL; if (ifhead == NULL) { ifhead = ifcur = this; } else { ifcur->next = this; ifcur = this; } return 0; } int iftable_print(void) { struct iftable *this; for (this = ifhead; this != NULL; this = this->next) { fprintf (stderr,"index : %u\n",this->index); fprintf (stderr,"ifname : %s\n",this->name); } fprintf (stderr,"-------------\n"); return 0; } int get_interfaces(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) { unsigned char *ptr; struct ifinfomsg *ifi = NLMSG_DATA(n); /* skip nlmsghdr + ifinfomsg + length + type */ ptr = (unsigned char *)ifi; ptr += sizeof(struct ifinfomsg) + 4; if (strstr(ptr,"eth") || strstr(ptr,"atm")) { iftable_insert(ifi->ifi_index, ptr); } return 0; } int dump_interfaces(void) { if (rtnl_wilddump_request(&rth, AF_UNSPEC, RTM_GETLINK) < 0) { perror("Cannot send dump request"); exit(1); } if (rtnl_dump_filter(&rth, get_interfaces, stdout, NULL, NULL) < 0) { fprintf(stderr, "Dump terminated\n"); exit(1); } return 0; } /* ----------------------------------------------------------------------------- */ int classifier_clear(void) { struct classifiertable *this, *ptr; this = clhead; while (this != NULL) { if (this->six != NULL) { free(this->six); } if (this->tcs != NULL) { free(this->tcs); } ptr = this; this = this->next; free(ptr); } clhead = clcur = NULL; sixtupleclfr_count = statistics_count = classifier_count = 0; return 0; } int classifier_insert(int ifindex, int handle) { struct classifiertable *this; this = (struct classifiertable *)calloc(1, sizeof(struct classifiertable)); this->ifindex = ifindex; this->ifdirection = 2; // default ifdirection = outbound (2) this->level = 0; this->handle = handle; this->parent = 0; this->filter = 0; this->precedence = 0; this->status = TRUE; // FALSE; this->six = (struct sixtupletable *)calloc(1, sizeof(struct sixtupletable)); /* for non-zero default sixtuple values */ this->six->dstPortMax = 65535; this->six->srcPortMax = 65535; this->tcs = (struct statisticstable *)calloc(1, sizeof(struct statisticstable)); this->next = NULL; if (clhead == NULL) { clhead = this; } else { clcur = clhead; clhead = this; clhead->next = clcur; } classifier_count++; return 0; } int classifier_insert_name(char *name) { strcpy(clhead->name,name); /* ingress means ifdirection is inbound */ if (!strcmp(name,"ingress")) { clhead->ifdirection = 1; } return 0; } int classifier_insert_dscp(unsigned char dscp) { if (clhead->six != NULL) { clhead->six->DSCP = dscp; } return 0; } int classifier_insert_tcs(struct tc_stats *tcs) { struct statisticstable *this; this = clhead->tcs; this->bytes = tcs->bytes; this->packets = tcs->packets; this->drops = tcs->drops; this->overlimits = tcs->overlimits; this->bps = tcs->bps; this->pps = tcs->pps; this->qlen = tcs->qlen; this->backlog = tcs->backlog; return 0; } int classifiertable_print(void) { struct classifiertable *this; unsigned short major, minor; for (this = clhead; this != NULL; this = this->next) { if (this->status == TRUE) { fprintf (stderr,"ifindex : %u\n",this->ifindex); fprintf (stderr,"ifdirection : %u\n",this->ifdirection); fprintf (stderr,"name : %s\n",this->name); fprintf (stderr,"level : %u\n",this->level); major = TC_H_MAJ(this->handle) >> 16; minor = TC_H_MIN(this->handle); fprintf (stderr,"handle : %x:%x \n",major,minor); major = TC_H_MAJ(this->parent) >> 16; minor = TC_H_MIN(this->parent); fprintf (stderr,"parent : %x:%x\n",major,minor); major = TC_H_MAJ(this->classid) >> 16; minor = TC_H_MIN(this->classid); fprintf (stderr,"classid : %x:%x\n",major,minor); fprintf (stderr,"filter : %u\n",this->filter); fprintf (stderr,"precedence : %u\n",this->precedence); fprintf (stderr,"status : %u\n",this->status); if (this->tcs != NULL) { fprintf (stderr,"tcstats bytes : %Lu \n",this->tcs->bytes); fprintf (stderr,"tcstats packets : %u \n",this->tcs->packets); fprintf (stderr,"tcstats drops : %u \n",this->tcs->drops); fprintf (stderr,"tcstats overlimits : %u \n",this->tcs->overlimits); fprintf (stderr,"tcstats bps : %u \n",this->tcs->bps); fprintf (stderr,"tcstats pps : %u \n",this->tcs->pps); fprintf (stderr,"tcstats qlen : %u \n",this->tcs->qlen); fprintf (stderr,"tcstats backlog : %u \n",this->tcs->backlog); } if (this->six != NULL) { fprintf (stderr,"sixtuple DSCP : %x \n",this->six->DSCP); } fprintf (stderr,"---------------\n"); } } return 0; } /* ----------------------------------------------------------------------------- */ int mark_tc_enable(int ifindex) { struct iftable *this; for (this = ifhead; this != NULL; this = this->next) { if ((this->index == ifindex) && (this->tcEnable == FALSE)) { this->tcEnable = TRUE; return 0; } } return 1; } int print_qdisc(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) { int i, length; unsigned char *ptr; struct tcmsg *t = NLMSG_DATA(n); struct rtattr *rta; struct tc_stats *tcs; char kind_str[16]; unsigned short major, minor; ptr = (unsigned char *)n; length = n->nlmsg_len; /* if (current_interface && current_interface != t->tcm_ifindex) return 0; */ /* fprintf (stderr,"ifindex : %d \n",t->tcm_ifindex); fprintf (stderr,"handle : "); major = TC_H_MAJ(t->tcm_handle) >> 16; minor = TC_H_MIN(t->tcm_handle); fprintf (stderr,"%x:%x \n",major,minor); */ /* fprintf (stderr,"parent : "); major = TC_H_MAJ(t->tcm_parent) >> 16; minor = TC_H_MIN(t->tcm_parent); fprintf (stderr,"%x:%x \n",major,minor); */ ptr = (unsigned char *)t; ptr += sizeof(struct tcmsg); length -= sizeof(struct nlmsghdr) + sizeof(struct tcmsg); mark_tc_enable(t->tcm_ifindex); classifier_insert(t->tcm_ifindex,t->tcm_handle); /* -------------------------------------------------------------- */ memset(kind_str,0,16); while (length > 4) { /* TCA enumeration 0 TCA_UNSPEC, 1 TCA_KIND, 2 TCA_OPTIONS, 3 TCA_STATS, 4 TCA_XSTATS, 5 TCA_RATE, */ rta = (struct rtattr *)ptr; /* switch (rta->rta_type) { case 0 : fprintf (stderr,"TCA_UNSPEC"); break; case 1 : fprintf (stderr,"TCA_KIND"); break; case 2 : fprintf (stderr,"TCA_OPTIONS"); break; case 3 : fprintf (stderr,"TCA_STATS"); break; case 4 : fprintf (stderr,"TCA_XSTATS"); break; case 5 : fprintf (stderr,"TCA_RATE"); break; } fprintf (stderr,"\n"); */ ptr += 4; length -= 4; /* payload */ if (rta->rta_type == 1) { /* fprintf (stderr,"\"%s\"\n",ptr); */ strcpy (kind_str, ptr); classifier_insert_name(kind_str); } else if (rta->rta_type == 3) { tcs = (struct tc_stats *)ptr; /* struct tc_stats (in linux/pkt_sched.h) __u64 bytes; NUmber of enqueues bytes __u32 packets; Number of enqueued packets __u32 drops; Packets dropped because of lack of resources __u32 overlimits; Number of throttle events when this flow goes out of allocated bandwidth __u32 bps; Current flow byte rate __u32 pps; Current flow packet rate __u32 qlen; __u32 backlog; */ /* fprintf (stderr,"\n"); fprintf (stderr,"tcstats bytes : %Lu \n",(unsigned long long)tcs->bytes); fprintf (stderr,"tcstats packets : %u \n",tcs->packets); fprintf (stderr,"tcstats drops : %u \n",tcs->drops); fprintf (stderr,"tcstats overlimits : %u \n",tcs->overlimits); fprintf (stderr,"tcstats bps : %u \n",tcs->bps); fprintf (stderr,"tcstats pps : %u \n",tcs->pps); fprintf (stderr,"tcstats qlen : %u \n",tcs->qlen); fprintf (stderr,"tcstats backlog : %u \n",tcs->backlog); */ classifier_insert_tcs(tcs); } /* go to the next rtattr */ ptr += rta->rta_len-4; length -= rta->rta_len-4; } return 0; } int dump_qdisc(void) { struct tcmsg t; memset(&t, 0, sizeof(t)); t.tcm_family = AF_UNSPEC; /* t.tcm_ifindex = ifindex; */ /* Address Family : Unspecified -- 0 in /usr/include/linux/socket.h */ if (rtnl_dump_request(&rth, RTM_GETQDISC, &t, sizeof(t)) < 0) { perror("Cannot send dump request"); exit(1); } /* RTM_GETQDISC = 0x10 + 22 = 38 in /usr/include/linux/rtnetlink.h */ if (rtnl_dump_filter(&rth, print_qdisc, stdout, NULL, NULL) < 0) { fprintf(stderr, "Dump terminated\n"); exit(1); } return 0; } int check_qdisc(void) { /* struct iftable *this; */ dump_qdisc(); /* for (this = ifhead; this != NULL; this = this->next) { current_interface = this->index; tc_enable_flag = -1; dump_qdisc(this->index); if (tc_enable_flag == -1) { this->tcEnable = FALSE; } else { this->tcEnable = TRUE; } } */ return 0; } /* ----------------------------------------------------------------------------- */ int print_dsmark_opt(unsigned char *ptr, int length) { struct rtattr *rta; int i; unsigned char dscp; while (length > 4) { rta = (struct rtattr *)ptr; ptr += 4; length -= 4; if (rta->rta_type == 5) { dscp = *((unsigned char *)ptr); dscp = dscp >> 3; classifier_insert_dscp(dscp); } /* go to the next rtattr */ ptr += rta->rta_len-4; length -= rta->rta_len-4; /* skip padding */ ptr += 3; length -= 3; } return 0; } int modify_classifier_by_class(int classid, int parent, int handle) { struct classifiertable *this; for (this = clhead; this != NULL; this = this->next) { if (this->handle == handle) { this->status = TRUE; this->parent = parent; this->classid = classid; // classifier_count++; return 0; } } return 0; } int check_handle(int handle) { struct classifiertable *this; for (this = clhead; this != NULL; this = this->next) { if (this->handle == handle) { return 1; } } return 0; } int print_class(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) { int length; unsigned char *ptr; struct tcmsg *t = NLMSG_DATA(n); char kind_str[16]; struct rtattr *rta; struct tc_stats *tcs; /* unsigned short major, minor; */ ptr = (unsigned char *)n; length = n->nlmsg_len; ptr = (unsigned char *)t; ptr += sizeof(struct tcmsg); length -= sizeof(struct nlmsghdr) + sizeof(struct tcmsg); if (t->tcm_info != 0) { modify_classifier_by_class(t->tcm_handle,t->tcm_parent,t->tcm_info); return 0; } if (check_handle(t->tcm_handle)) { return 0; } /* --------------------------------------------------- */ /* from now on, new classifier should be installed */ classifier_insert(t->tcm_ifindex,t->tcm_handle); memset(kind_str,0,16); while (length > 4) { rta = (struct rtattr *)ptr; /* skip rtattr header */ ptr += 4; length -= 4; /* payload */ if (rta->rta_type == 1) { strcpy (kind_str, ptr); classifier_insert_name(kind_str); } if (rta->rta_type == 2) { if (strcmp(kind_str,"dsmark") == 0) { print_dsmark_opt(ptr, rta->rta_len-4); } } else if (rta->rta_type == 3) { tcs = (struct tc_stats *)ptr; classifier_insert_tcs(tcs); } /* go to the next rtattr */ ptr += rta->rta_len-4; length -= rta->rta_len-4; } return 0; } int dump_class(int ifindex) { struct tcmsg t; memset(&t, 0, sizeof(t)); t.tcm_family = AF_UNSPEC; t.tcm_ifindex = ifindex; if (rtnl_dump_request(&rth, RTM_GETTCLASS, &t, sizeof(t)) < 0) { perror("Cannot send dump request"); exit(1); } if (rtnl_dump_filter(&rth, print_class, stdout, NULL, NULL) < 0) { fprintf(stderr, "Dump terminated\n"); exit(1); } return 0; } int check_class(void) { struct iftable *this; for (this = ifhead; this != NULL; this = this->next) { if (this->tcEnable == TRUE) { dump_class(this->index); } } return 0; } /* ----------------------------------------------------------------------------- */ int insert_dscp(int classid, int dscp) { struct classifiertable *this; struct sixtupletable *six; for (this = clhead; this != NULL; this = this->next) { if (this->classid == classid) { six = this->six; six->dstAddr = 0; six->dstAddrMask = 0; six->srcAddr = 0; six->srcAddrMask = 0; six->DSCP = dscp; six->protocol = 0; six->dstPortMin = 0; six->dstPortMax = 65535; six->srcPortMin = 0; six->srcPortMax = 65535; } } return 0; } int print_tcindex_fopt(unsigned char *ptr, int length) { struct rtattr *rta; while (length > 4) { rta = (struct rtattr *)ptr; // skip length and type ptr += 4; length -= 4; /* TCA_TCINDEX_CLASSID */ if (rta->rta_type == 5) { __u32 classid; classid = *((__u32 *)ptr); return classid; } } return 0; } int print_filter(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) { int i, length, classid; unsigned char *ptr; struct tcmsg *t = NLMSG_DATA(n); struct rtattr *rta; struct tc_stats *tcs; char kind_str[16]; unsigned short major, minor; printf ("I'm here...\n"); ptr = (unsigned char *)n; length = n->nlmsg_len; ptr = (unsigned char *)t; ptr += sizeof(struct tcmsg); length -= sizeof(struct nlmsghdr) + sizeof(struct tcmsg); memset(kind_str,0,16); while (length > 4) { rta = (struct rtattr *)ptr; // skip length and type ptr += 4; length -= 4; // TCA_KIND if (rta->rta_type == 1) { strcpy (kind_str, ptr); // TCA_OPTION } else if (rta->rta_type == 2) { if (strcmp(kind_str,"tcindex") == 0) { classid = print_tcindex_fopt(ptr, rta->rta_len-4); if (classid > 0) { insert_dscp(classid, t->tcm_handle >> 1); } } } ptr += rta->rta_len-4; length -= rta->rta_len-4; } return 0; } int dump_filter(int ifindex, int parent) { struct tcmsg t; memset(&t, 0, sizeof(t)); t.tcm_family = AF_UNSPEC; t.tcm_ifindex = ifindex; t.tcm_parent = (__u32)parent; printf ("dump filter : ifindex = %d, parent = %X\n", t.tcm_ifindex, t.tcm_parent); if (rtnl_dump_request(&rth, RTM_GETTFILTER, &t, sizeof(t)) < 0) { perror("Cannot send dump request"); exit(1); } if (rtnl_dump_filter(&rth, print_filter, stdout, NULL, NULL) < 0) { fprintf(stderr, "Dump terminated\n"); exit(1); } return 0; } int check_filter(void) { struct iftable *tif; struct classifiertable *this; int lastparent = -1; /* dump parent filter */ for (tif = ifhead; tif != NULL; tif = tif->next) { dump_filter(tif->index,TC_H_ROOT); } /* dump specific filter */ for (this = clhead; this != NULL; this = this->next) { if ((this->status == TRUE) && (this->parent != lastparent)) { dump_filter(this->ifindex,this->parent); lastparent = this->parent; } } return 0; } /* ----------------------------------------------------------------------------- */ int find_classifier_ifindex(int index) { struct classifiertable *this; int i; i=0; for (this = clhead; this != NULL; this = this->next) { if (this->status == TRUE) { if (index == i) { return this->ifindex; } i++; } } return 0; } int find_classifier_ifdirection(int index) { struct classifiertable *this; int i; i=0; for (this = clhead; this != NULL; this = this->next) { if (this->status == TRUE) { if (index == i) { return this->ifdirection; } i++; } } return 0; } int find_classifier_classid(int index) { struct classifiertable *this; int i; i=0; for (this = clhead; this != NULL; this = this->next) { if (this->status == TRUE) { if (index == i) { return this->classid; } i++; } } return 0; } int find_classifier_precedence(int index) { struct classifiertable *this; int i; i=0; for (this = clhead; this != NULL; this = this->next) { if (this->status == TRUE) { if (index == i) { return this->precedence; } i++; } } return 0; } int find_sixtuple_dscp(int index) { struct classifiertable *this; int i; i=0; for (this = clhead; this != NULL; this = this->next) { if (this->status == TRUE) { if ((index == i) && (this->six != NULL)) { return this->six->DSCP; } i++; } } return 0; } int find_statistics_bytes(int index) { struct classifiertable *this; int i; i=0; for (this = clhead; this != NULL; this = this->next) { if (this->status == TRUE) { if ((index == i) && (this->tcs != NULL)) { return (int)(this->tcs->bytes); } i++; } } return 0; } int find_statistics_packets(int index) { struct classifiertable *this; int i; i=0; for (this = clhead; this != NULL; this = this->next) { if (this->status == TRUE) { if ((index == i) && (this->tcs != NULL)) { return this->tcs->packets; } i++; } } return 0; } int find_statistics_drops(int index) { struct classifiertable *this; int i; i=0; for (this = clhead; this != NULL; this = this->next) { if (this->status == TRUE) { if ((index == i) && (this->tcs != NULL)) { return this->tcs->drops; } i++; } } return 0; } int find_statistics_overlimits(int index) { struct classifiertable *this; int i; i=0; for (this = clhead; this != NULL; this = this->next) { if (this->status == TRUE) { if ((index == i) && (this->tcs != NULL)) { return this->tcs->overlimits; } i++; } } return 0; } int find_statistics_bps(int index) { struct classifiertable *this; int i; i=0; for (this = clhead; this != NULL; this = this->next) { if (this->status == TRUE) { if ((index == i) && (this->tcs != NULL)) { return this->tcs->bps; } i++; } } return 0; } int find_statistics_pps(int index) { struct classifiertable *this; int i; i=0; for (this = clhead; this != NULL; this = this->next) { if (this->status == TRUE) { if ((index == i) && (this->tcs != NULL)) { return this->tcs->pps; } i++; } } return 0; } int find_statistics_qlen(int index) { struct classifiertable *this; int i; i=0; for (this = clhead; this != NULL; this = this->next) { if (this->status == TRUE) { if ((index == i) && (this->tcs != NULL)) { return this->tcs->qlen; } i++; } } return 0; } int find_statistics_backlog(int index) { struct classifiertable *this; int i; i=0; for (this = clhead; this != NULL; this = this->next) { if (this->status == TRUE) { if ((index == i) && (this->tcs != NULL)) { return this->tcs->backlog; } i++; } } return 0; } /* ----------------------------------------------------------------------------- */ int refresh_tc(void) { struct timeval tv; struct timezone tz; static long timestamp = 0; int refresh_flag; refresh_flag = FALSE; gettimeofday(&tv,&tz); // fprintf(stderr,"timestamp = %dL, now = %dL\n", timestamp, tv.tv_sec); if (timestamp == 0) { timestamp = tv.tv_sec; refresh_flag = TRUE; } else if (tv.tv_sec - timestamp > MINREFRESH) { timestamp = tv.tv_sec; refresh_flag = TRUE; } if (refresh_flag) { classifier_clear(); check_qdisc(); check_class(); check_filter(); sixtupleclfr_count = statistics_count = classifier_count; classifiertable_print(); } } /* ----------------------------------------------------------------------------- */ void init_diffserv(void) { fprintf (stderr, "DiffServ agent started...\n"); ifhead = ifcur = NULL; clhead = clcur = NULL; init_rtnl(); dump_interfaces(); iftable_print(); check_qdisc(); check_class(); check_filter(); sixtupleclfr_count = statistics_count = classifier_count; classifiertable_print(); /* sleep(5); refresh_tc(); */ }