`

Linux内核中流量控制(15)

阅读更多
本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,严禁用于任何商业用途。
msn: yfydz_no1@hotmail.com
来源:http://yfydz.cublog.cn

5.15. Qdisc的netlink控制

各网卡的Qdisc的用户层操作控制是通过rtnetlink接口实现用户空间和内核之间的通信的: rtnetlink_link, rtnetlink是专门针对路由控制的netlink接口.
/* include/linux/rtnetlink.h */
struct rtnetlink_link
{
// 就两个成员函数, 操作和输出
 int (*doit)(struct sk_buff *, struct nlmsghdr*, void *attr);
 int (*dumpit)(struct sk_buff *, struct netlink_callback *cb);
};

// 全局数组, 具体在 net/core/rtnetlink.c中定义
extern struct rtnetlink_link * rtnetlink_links[NPROTO];

其中的两个成员定义如下:
/* net/core/rtnetlink.c */

void __init rtnetlink_init(void)
{
......
 rtnetlink_links[PF_UNSPEC] = link_rtnetlink_table;
 rtnetlink_links[PF_PACKET] = link_rtnetlink_table;
......
}

其中link_rtnetlink_table是一个数组, 定义为:
static struct rtnetlink_link link_rtnetlink_table[RTM_NR_MSGTYPES] =
{
// 数组基本元素, 处理路由, 邻居ARP等相关信息, 这些不是本文重点,
// 只是Qdisc的相关操作也是定义在这个数组中
 [RTM_GETLINK     - RTM_BASE] = { .doit   = rtnl_getlink,
      .dumpit = rtnl_dump_ifinfo  },
 [RTM_SETLINK     - RTM_BASE] = { .doit   = rtnl_setlink   },
 [RTM_GETADDR     - RTM_BASE] = { .dumpit = rtnl_dump_all  },
 [RTM_GETROUTE    - RTM_BASE] = { .dumpit = rtnl_dump_all  },
 [RTM_NEWNEIGH    - RTM_BASE] = { .doit   = neigh_add   },
 [RTM_DELNEIGH    - RTM_BASE] = { .doit   = neigh_delete   },
 [RTM_GETNEIGH    - RTM_BASE] = { .dumpit = neigh_dump_info  },
#ifdef CONFIG_FIB_RULES
 [RTM_NEWRULE     - RTM_BASE] = { .doit   = fib_nl_newrule  },
 [RTM_DELRULE     - RTM_BASE] = { .doit   = fib_nl_delrule  },
#endif
 [RTM_GETRULE     - RTM_BASE] = { .dumpit = rtnl_dump_all  },
 [RTM_GETNEIGHTBL - RTM_BASE] = { .dumpit = neightbl_dump_info  },
 [RTM_SETNEIGHTBL - RTM_BASE] = { .doit   = neightbl_set   },
};
 
5.15.1 初始化

初始化过程是定义对应tc的qdisc和class的操作命令的处理函数:
/* net/sched/sch_api.c */
static int __init pktsched_init(void)
{
 struct rtnetlink_link *link_p;
// 流控调度的时钟初始化
#ifdef CONFIG_NET_SCH_CLK_CPU
 if (psched_calibrate_clock() < 0)
  return -1;
#elif defined(CONFIG_NET_SCH_CLK_JIFFIES)
 psched_tick_per_us = HZ<<PSCHED_JSCALE;
 psched_us_per_tick = 1000000;
#endif
// 使用PF_UNSPEC(0)号rtnetlink_links元素用来作为QDISC操作的接口
 link_p = rtnetlink_links[PF_UNSPEC];
 /* Setup rtnetlink links. It is made here to avoid
    exporting large number of public symbols.
  */
// link_p将指向link_rtnetlink_table数组
 if (link_p) {
// 对数组中流控相关元素进行赋值
// Qdisc操作, 也就是对应tc qdisc add/modify等操作
  link_p[RTM_NEWQDISC-RTM_BASE].doit = tc_modify_qdisc;
// 删除/获取Qdisc操作
  link_p[RTM_DELQDISC-RTM_BASE].doit = tc_get_qdisc;
  link_p[RTM_GETQDISC-RTM_BASE].doit = tc_get_qdisc;
// 获取Qdisc信息, 也就是对应tc qdisc show
  link_p[RTM_GETQDISC-RTM_BASE].dumpit = tc_dump_qdisc;
// class操作, 也就是对应tc class add/delete/modify/get等操作, 在后续文章中分析
  link_p[RTM_NEWTCLASS-RTM_BASE].doit = tc_ctl_tclass;
  link_p[RTM_DELTCLASS-RTM_BASE].doit = tc_ctl_tclass;
  link_p[RTM_GETTCLASS-RTM_BASE].doit = tc_ctl_tclass;
  link_p[RTM_GETTCLASS-RTM_BASE].dumpit = tc_dump_tclass;
 }
// 登记FIFO流控处理, 这是网卡设备基本流控方法, 缺省必有的
 register_qdisc(&pfifo_qdisc_ops);
 register_qdisc(&bfifo_qdisc_ops);
 proc_net_fops_create("psched", 0, &psched_fops);
 return 0;
}

5.15.2 相关操作

以下函数中用到的Qdisc操作函数可见本系列第一篇, 第4节

5.15.2.1 创建/修改qdisc

/*
   Create/change qdisc.
 */
static int tc_modify_qdisc(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
{
 struct tcmsg *tcm;
 struct rtattr **tca;
 struct net_device *dev;
 u32 clid;
 struct Qdisc *q, *p;
 int err;
replay:
 /* Reinit, just in case something touches this. */
// tc消息指针
 tcm = NLMSG_DATA(n);
 tca = arg;
// class id
 clid = tcm->tcm_parent;
 q = p = NULL;
// 该tc命令针对的网卡
 if ((dev = __dev_get_by_index(tcm->tcm_ifindex)) == NULL)
  return -ENODEV;
 if (clid) {
// 指定了类别ID的情况
  if (clid != TC_H_ROOT) {
// 如果不是根节点
   if (clid != TC_H_INGRESS) {
// 非ingress节点时, 根据类别ID的高16位查找Qdisc节点
    if ((p = qdisc_lookup(dev, TC_H_MAJ(clid))) == NULL)
     return -ENOENT;
// 获取p节点的叶子节点, 将调用p->ops->cl_ops->leaf()函数
    q = qdisc_leaf(p, clid);
   } else { /*ingress */
// 使用设备ingress流控
    q = dev->qdisc_ingress;
   }
  } else {
// 根节点情况下流控用的是设备的qdisc_sleeping
   q = dev->qdisc_sleeping;
  }
  /* It may be default qdisc, ignore it */
// 如果找到的Qdisc的句柄为0, 放弃q
  if (q && q->handle == 0)
   q = NULL;
  if (!q || !tcm->tcm_handle || q->handle != tcm->tcm_handle) {
// 没找到Qdisc节点, 或没在tc消息中指定句柄值, 或者找到的Qdisc句柄和tc消息中
// 的句柄不同
   if (tcm->tcm_handle) {
// TC指定了句柄
// 如果Qdisc存在但不是更新命令, 返回对象存在错误
    if (q && !(n->nlmsg_flags&NLM_F_REPLACE))
     return -EEXIST;
// TC句柄低16位不能位0
    if (TC_H_MIN(tcm->tcm_handle))
     return -EINVAL;
// 根据TC句柄查找该设备上的Qdisc, 找不到的话跳转到创建新节点操作
    if ((q = qdisc_lookup(dev, tcm->tcm_handle)) == NULL)
     goto create_n_graft;
// 找到但设置了NLM_F_EXCL排斥标志, 返回对象存在错误
    if (n->nlmsg_flags&NLM_F_EXCL)
     return -EEXIST;
// 比较TC命令中的算法名称和Qdisc名称算法相同
    if (tca[TCA_KIND-1] && rtattr_strcmp(tca[TCA_KIND-1], q->ops->id))
     return -EINVAL;
// 检查算法出现回环情况, p是用clid找到的Qdisc
    if (q == p ||
        (p && check_loop(q, p, 0)))
     return -ELOOP;
// 新找到的Qdisc有效, 转到嫁接操作
    atomic_inc(&q->refcnt);
    goto graft;
   } else {
// 没指定TC句柄, 如果没找到Qdisc, 跳转到创建新节点
    if (q == NULL)
     goto create_n_graft;
    /* This magic test requires explanation.
     *
     *   We know, that some child q is already
     *   attached to this parent and have choice:
     *   either to change it or to create/graft new one.
     *
     *   1. We are allowed to create/graft only
     *   if CREATE and REPLACE flags are set.
     *
     *   2. If EXCL is set, requestor wanted to say,
     *   that qdisc tcm_handle is not expected
     *   to exist, so that we choose create/graft too.
     *
     *   3. The last case is when no flags are set.
     *   Alas, it is sort of hole in API, we
     *   cannot decide what to do unambiguously.
     *   For now we select create/graft, if
     *   user gave KIND, which does not match existing.
     */
// 检查各种标志是否冲突, Qdisc名称是否正确
    if ((n->nlmsg_flags&NLM_F_CREATE) &&
        (n->nlmsg_flags&NLM_F_REPLACE) &&
        ((n->nlmsg_flags&NLM_F_EXCL) ||
         (tca[TCA_KIND-1] &&
          rtattr_strcmp(tca[TCA_KIND-1], q->ops->id))))
     goto create_n_graft;
   }
  }
 } else {
// 如果没指定类别ID, 从tc消息的句柄来查找Qdisc
  if (!tcm->tcm_handle)
   return -EINVAL;
  q = qdisc_lookup(dev, tcm->tcm_handle);
 }
// 到这里是属于Qdisc修改操作
 /* Change qdisc parameters */
// 没找到Qdisc节点, 返回错误
 if (q == NULL)
  return -ENOENT;
// 找到Qdisc节点, 但设置了NLM_F_EXCL(排斥)标志, 返回对象存在错误
 if (n->nlmsg_flags&NLM_F_EXCL)
  return -EEXIST;
// 检查找到的Qdisc节点的名称和tc中指定的是否匹配
 if (tca[TCA_KIND-1] && rtattr_strcmp(tca[TCA_KIND-1], q->ops->id))
  return -EINVAL;
// 修改Qdisc参数
 err = qdisc_change(q, tca);
 if (err == 0)
  qdisc_notify(skb, n, clid, NULL, q);
 return err;
create_n_graft:
// 创建新Qdisc节点
// 如果TC命令中没有创建标志, 返回错误
 if (!(n->nlmsg_flags&NLM_F_CREATE))
  return -ENOENT;
// 创建新Qdisc节点
 if (clid == TC_H_INGRESS)
  q = qdisc_create(dev, tcm->tcm_parent, tca, &err);
        else
  q = qdisc_create(dev, tcm->tcm_handle, tca, &err);
 if (q == NULL) {
// 创建失败, 如果不是EAGAIN(重来一次), 返回失败
  if (err == -EAGAIN)
   goto replay;
  return err;
 }
graft:
// 嫁接操作
 if (1) {
  struct Qdisc *old_q = NULL;
// 进行嫁接操作, 返回老节点
  err = qdisc_graft(dev, p, clid, q, &old_q);
  if (err) {
// 失败, 释放新建立的Qdisc
   if (q) {
    spin_lock_bh(&dev->queue_lock);
    qdisc_destroy(q);
    spin_unlock_bh(&dev->queue_lock);
   }
   return err;
  }
// Qdisc通告
  qdisc_notify(skb, n, clid, old_q, q);
  if (old_q) {
// 如果存在老Qdisc节点, 释放之
   spin_lock_bh(&dev->queue_lock);
   qdisc_destroy(old_q);
   spin_unlock_bh(&dev->queue_lock);
  }
 }
 return 0;
}

5.15.2.2 获取/删除qdisc
/*
 * Delete/get qdisc.
 */
static int tc_get_qdisc(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
{
 struct tcmsg *tcm = NLMSG_DATA(n);
 struct rtattr **tca = arg;
 struct net_device *dev;
// class id
 u32 clid = tcm->tcm_parent;
 struct Qdisc *q = NULL;
 struct Qdisc *p = NULL;
 int err;
// 根据TC参数中的网卡索引号查找网卡设备
 if ((dev = __dev_get_by_index(tcm->tcm_ifindex)) == NULL)
  return -ENODEV;
// 根据类别ID或TC句柄查找Qdisc, 和上面函数类似
 if (clid) {
  if (clid != TC_H_ROOT) {
   if (TC_H_MAJ(clid) != TC_H_MAJ(TC_H_INGRESS)) {
    if ((p = qdisc_lookup(dev, TC_H_MAJ(clid))) == NULL)
     return -ENOENT;
    q = qdisc_leaf(p, clid);
   } else { /* ingress */
    q = dev->qdisc_ingress;
                        }
  } else {
   q = dev->qdisc_sleeping;
  }
  if (!q)
   return -ENOENT;
  if (tcm->tcm_handle && q->handle != tcm->tcm_handle)
   return -EINVAL;
 } else {
  if ((q = qdisc_lookup(dev, tcm->tcm_handle)) == NULL)
   return -ENOENT;
 }
// 检查找到的Qdisc名称和TC命令中指定的是否一致
 if (tca[TCA_KIND-1] && rtattr_strcmp(tca[TCA_KIND-1], q->ops->id))
  return -EINVAL;
// 删除Qdisc操作
 if (n->nlmsg_type == RTM_DELQDISC) {
// 必须指定类别ID
  if (!clid)
   return -EINVAL;
// 如果找到的Qdisc句柄为0, 返回错误
  if (q->handle == 0)
   return -ENOENT;
// 进行Qdisc嫁接操作, 新节点是NULL, 即将叶子节点替换为NULL, 即删除了原叶子节点
// 原叶子节点返回到q
  if ((err = qdisc_graft(dev, p, clid, NULL, &q)) != 0)
   return err;
  if (q) {
// 释放原叶子节点
   qdisc_notify(skb, n, clid, q, NULL);
   spin_lock_bh(&dev->queue_lock);
   qdisc_destroy(q);
   spin_unlock_bh(&dev->queue_lock);
  }
 } else {
// 非删除操作, 通告一下, q作为获得的Qdisc参数返回
  qdisc_notify(skb, n, clid, NULL, q);
 }
 return 0;
}

// 发送Qdisc通知信息, new是处理后新Qdisc节点信息, old是处理前老节点信息
static int qdisc_notify(struct sk_buff *oskb, struct nlmsghdr *n,
   u32 clid, struct Qdisc *old, struct Qdisc *new)
{
 struct sk_buff *skb;
 u32 pid = oskb ? NETLINK_CB(oskb).pid : 0;
// 分配netlink数据包
 skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
 if (!skb)
  return -ENOBUFS;
 if (old && old->handle) {
// 填充老Qdisc的信息
  if (tc_fill_qdisc(skb, old, clid, pid, n->nlmsg_seq, 0, RTM_DELQDISC) < 0)
   goto err_out;
 }
 if (new) {
// 填充新Qdisc的信息
  if (tc_fill_qdisc(skb, new, clid, pid, n->nlmsg_seq, old ? NLM_F_REPLACE : 0, RTM_NEWQDISC) < 0)
   goto err_out;
 }
// 发送数据包
 if (skb->len)
  return rtnetlink_send(skb, pid, RTNLGRP_TC, n->nlmsg_flags&NLM_F_ECHO);
err_out:
// 错误处理, 释放数据包
 kfree_skb(skb);
 return -EINVAL;
}

5.15.2.3 输出网卡qdisc参数

static int tc_dump_qdisc(struct sk_buff *skb, struct netlink_callback *cb)
{
 int idx, q_idx;
 int s_idx, s_q_idx;
 struct net_device *dev;
 struct Qdisc *q;
// 起始网卡索引
 s_idx = cb->args[0];
// 起始Qdisc索引
 s_q_idx = q_idx = cb->args[1];
 read_lock(&dev_base_lock);
// 遍历所有网卡
 for (dev=dev_base, idx=0; dev; dev = dev->next, idx++) {
// 索引值小于所提供的起始索引值, 跳过
// 这个索引和网卡的索引号应该没啥关系
  if (idx < s_idx)
   continue;
// 索引值大于所提供的起始索引值, 将起始Qdisc索引清零
  if (idx > s_idx)
   s_q_idx = 0;
  read_lock(&qdisc_tree_lock);
// q_idx清零, 这样前面也用不着在初始化时赋值
  q_idx = 0;
// 遍历该网卡设备的所有Qdisc
  list_for_each_entry(q, &dev->qdisc_list, list) {
// 当前Qdisc索引小于起始Qdisc索引, 跳过
// 所以当idx > s_idx时, s_q_idx = 0, 只处理第一个Qdisc
// 当idx == s_idx时, 处理从s_q_idx开始的所有Qdisc
   if (q_idx < s_q_idx) {
    q_idx++;
    continue;
   }
// 填充Qdisc信息到数据包
   if (tc_fill_qdisc(skb, q, q->parent, NETLINK_CB(cb->skb).pid,
       cb->nlh->nlmsg_seq, NLM_F_MULTI, RTM_NEWQDISC) <= 0) {
    read_unlock(&qdisc_tree_lock);
    goto done;
   }
   q_idx++;
  }
  read_unlock(&qdisc_tree_lock);
 }
done:
 read_unlock(&dev_base_lock);
// 返回处理的所有网卡数和Qdisc数
 cb->args[0] = idx;
 cb->args[1] = q_idx;
 return skb->len;
}

// 填充Qdisc信息到skb数据包
static int tc_fill_qdisc(struct sk_buff *skb, struct Qdisc *q, u32 clid,
    u32 pid, u32 seq, u16 flags, int event)
{
 struct tcmsg *tcm;
 struct nlmsghdr  *nlh;
 unsigned char  *b = skb->tail;
 struct gnet_dump d;
// skb中的netlink数据头位置
 nlh = NLMSG_NEW(skb, pid, seq, event, sizeof(*tcm), flags);
// TC信息数据头位置
 tcm = NLMSG_DATA(nlh);
// 填充TC信息参数
 tcm->tcm_family = AF_UNSPEC;
 tcm->tcm__pad1 = 0;
 tcm->tcm__pad2 = 0;
 tcm->tcm_ifindex = q->dev->ifindex;
 tcm->tcm_parent = clid;
 tcm->tcm_handle = q->handle;
 tcm->tcm_info = atomic_read(&q->refcnt);
 RTA_PUT(skb, TCA_KIND, IFNAMSIZ, q->ops->id);
// Qdisc的输出函数
 if (q->ops->dump && q->ops->dump(q, skb) < 0)
  goto rtattr_failure;
 q->qstats.qlen = q->q.qlen;
// 准备开始拷贝统计信息
 if (gnet_stats_start_copy_compat(skb, TCA_STATS2, TCA_STATS,
   TCA_XSTATS, q->stats_lock, &d) < 0)
  goto rtattr_failure;
// 输出统计信息
 if (q->ops->dump_stats && q->ops->dump_stats(q, &d) < 0)
  goto rtattr_failure;
// 拷贝基本统计信息, 流控速率统计信息, 队列统计信息
 if (gnet_stats_copy_basic(&d, &q->bstats) < 0 ||
#ifdef CONFIG_NET_ESTIMATOR
     gnet_stats_copy_rate_est(&d, &q->rate_est) < 0 ||
#endif
     gnet_stats_copy_queue(&d, &q->qstats) < 0)
  goto rtattr_failure;
// 结束封口操作 
 if (gnet_stats_finish_copy(&d) < 0)
  goto rtattr_failure;
 
 nlh->nlmsg_len = skb->tail - b;
 return skb->len;
nlmsg_failure:
rtattr_failure:
 skb_trim(skb, b - skb->data);
 return -1;
}
 

5.16 Qdisc小结
 
关于流控(Qdisc)的分析就此告一段落, 后面将继续分析分类(class), 过滤(filter)和动作(action)的处理过程.

...... 待续 ......
分享到:
评论

相关推荐

    基于Linux内核扩展模块的P2P流量控制

    基于Linux内核扩展模块的P2P流量控制

    基于Linux内核的BT流量控制的原理与实现.pdf

    基于Linux内核的BT流量控制的原理与实现.pdf

    基于Linux内核扩展模块的P2P流量控制.pdf

    基于Linux内核扩展模块的P2P流量控制.pdf

    Linux内核扩展模块的P2P流量控制方法与研究.pdf

    Linux内核扩展模块的P2P流量控制方法与研究.pdf

    Linux高级路由和流量控制

    15.8. 终极的流量控制:低延迟,高速上/下载 98 15.8.1. 为什么缺省设置不让人满意 99 15.8.2. 实际的脚本(CBQ) 100 15.8.3. 实际的脚本(HTB) 102 15.9. 为单个主机或子网限速 103 15.10. 一个完全NAT和QOS的范例...

    基于Linux LQL流量控制系统的研究与实现

    该方法摒弃了传统方法中所运用的TC命令解析,netlink传输,内核空间执行的3层结构,而直接在Linux内核的框架下,采用LQL库直接对内核进行操控,并改进了相关U32过滤器以对IP段的流量控制,从而实现对系统的智能流量控制。...

    编写Linuxc操作系统设备驱动程序概述

    在Linux内核的不断升级过程中,驱动程序的结构还是相对稳定。Linux的网络系统主要是基于BSD unix的socket机制 。在系统和驱动程序之间定义有专门的数据结构(sk_buff)进行数据的传递。系统里支持对发送数据和接收数据...

    xt_fset:扩展到Linux内核netfilter子系统(iptables)(插件),使您可以通过发送ICMP包远程操作linux内核ipset(向ipset中添加或删除一些ip地址)。

    xt_fset是linux内核netfilter子系统的内核模块和iptables扩展(插件),允许您通过发送控制ICMP数据包来远程操作linux内核ipset(在ipset中添加或删除一些ip地址)。 该插件的创建是对Linux内核netfilter子系统的...

    Linux Kernel v4.19.1 Stable.zip

    Linux Kernel主要新特性包括:合并了来自Android项目的内核代码,支持新的架构TI C6X,改进了Btrfs...网络优先控制组允许管理员动态设置网络流量的优先次序;支持EFI引导固件;改进内存管理,等等。 Linux Kernel截图

    Linux的高级路由和流量控制HOWTO-中文版

    虽然这些工具能够工作,但它们在 Linux2.2 和更高版本的内核上显 得有一些落伍。比如,现在 GRE 隧道已经成为了路由的一个主要概念,但却不 能通过上述工具来配置。 使用了 iproute2,隧道的配置与其他部分完全集成了。

    lksctp-rs:Rust 的 Linux 内核 SCTP 低级绑定

    流量控制和拥塞控制 此外,它还补充说: 多路复用流:通过单个连接,您可以多路复用多个信息流。 端点的多宿主:一个端点可以有多个 IP 地址,允许网络故障转移/可靠性(注意:它需要是一台机器,或者复制端点的...

    《精通Linux 设备驱动程序开发》.(Sreekrishnan).pdf

    15.2.3 流量控制315 15.3 缓冲区管理和并发控制315 15.4 设备实例:以太网nic316 15.5 isa网络驱动程序321 15.6 atm321 15.7 网络吞吐量322 15.7.1 驱动程序性能322 15.7.2 协议性能323 15.8 查看...

    精通LINUX设备驱动程序开发

    311 15.1.6 统计 312 15.1.7 配置 313 15.1.8 总线相关内容 314 15.2 与协议层会话 314 15.2.1 接收路径 314 15.2.2 发送路径 315 15.2.3 流量控制 315 15.3 缓冲区管理和并发控制 315 15.4 设备实例:...

    基于Linux 的防火墙技术研究

    络地址转换、流量控制及高级的包处理等。Netfilter/Iptables 系统采用模块化的架构方式,其主要模块 有:通用框架Netfilter、数据包选择系统、连接跟踪系统及NAT系统等等。 2.1 Netfilter/Iptables 系统工作原理 ...

    Linux模拟网络丢包与延迟的方法

    netem 与 tc: netem 是 Linux 2.6 及以上...tc 是 Linux 系统中的一个工具,全名为traffic control(流量控制)。tc 可以用来控制 netem 的工作模式,也就是说,如果想使用 netem ,需要至少两个条件,一个是内核中的

    ip route2 源码 第二代网络工具

    如果你仍在使用net-tools,而且尤其需要跟上新版Linux内核中的最新最重要的网络特性的话,那么是时候转到iproute2的阵营了。原因就在于使用iproute2可以做很多net-tools无法做到的事情。对于那些想要转到使用iproute...

    DarkShell_Linux-Win集群版V2014年

    Linux支持路由内核、2.6、3.1等普通内核,路由内核支持路由三大内核、Ubuntu、admin等,独立开发的Linux穿盾CC模式,SYN稳定发包100%,自启动,无需Root权限上线即可发包。 VIP版本攻击代码实时更新,通过服务器...

    Kali Linux渗透测试(安全牛).txt

    │ 任务085:答疑(Conky、、Linux4.4内核发布),手动漏洞挖掘.mp4 │ 任务086:手动漏洞挖掘(二).mp4 │ 任务087:手动漏洞挖掘(三).mp4 │ 任务088:手动漏洞挖掘(四).mp4 │ 任务089:KALI版本更新(第一个...

Global site tag (gtag.js) - Google Analytics