123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989 |
- /*
- * Monitoring code for network dropped packet alerts
- *
- * Copyright (C) 2018 SAMSUNG Electronics, Co,LTD
- */
- #include <net/ip.h>
- #include <net/tcp.h>
- #if defined(CONFIG_ANDROID_VENDOR_HOOKS)
- #include <trace/hooks/net.h>
- #endif
- #include <trace/events/skb.h>
- #include <net/dropdump.h>
- int debug_drd = 0;
- #define drd_info(fmt, ...) pr_info("drd: %s: " pr_fmt(fmt), __func__, ##__VA_ARGS__)
- #define drd_dbg(flag, ...) \
- do { \
- if (unlikely(debug_drd & flag)) { drd_info(__VA_ARGS__); } \
- else {} \
- } while (0)
- DEFINE_RATELIMIT_STATE(drd_ratelimit_print, 1 * HZ, 10);
- #define drd_limit(...) \
- do { \
- if (__ratelimit(&drd_ratelimit_print)) \
- drd_info(__VA_ARGS__); \
- } while (0)
- DEFINE_RATELIMIT_STATE(drd_ratelimit_pkt, 1 * HZ, 32);
- struct list_head ptype_log __read_mostly;
- EXPORT_SYMBOL_GPL(ptype_log);
- int support_dropdump;
- EXPORT_SYMBOL_GPL(support_dropdump);
- extern struct list_head ptype_all;
- struct st_item hmap[DRD_HSIZE];
- spinlock_t hlock;
- u64 hmap_count;
- u64 hdup_count;
- uint hmax_depth;
- u16 skip_count;
- u64 dropped_count;
- #ifdef DRD_WQ
- struct _drd_worker drd_worker;
- unsigned int budget_default = BUDGET_DEFAULT;
- unsigned int budget_limit;
- #define BUDGET_MAX (budget_default << 2)
- #define LIST_MAX (BUDGET_MAX << 2)
- #endif
- void init_st_item(struct st_item *item)
- {
- INIT_LIST_HEAD(&item->list);
- item->p = 0;
- item->matched = 0;
- item->st[0] = '\n';
- }
- int get_hkey(u64 *hvalue)
- {
- u64 key = 0;
- u64 src = *hvalue & 0x00000000ffffffff;
- while (src) {
- key += src & 0x000000ff;
- src >>= 8;
- }
- key %= DRD_HSIZE;
- return (int)key;
- }
- char *get_hmap(u64 *hvalue)
- {
- int hkey = get_hkey(hvalue);
- struct st_item *lookup = &hmap[hkey];
- uint depth = 1;
- do {
- drd_dbg(DEBUG_HASH, "hvalue search[%d]: <%pK|%pK|%pK> p:[%llx], hvalue:{%llx}\n",
- hkey, lookup, lookup->list.next, &hmap[hkey], lookup->p, *hvalue);
- if (lookup->p == *hvalue) {
- drd_dbg(DEBUG_HASH, "hvalue found: '%s'\n", lookup->st);
- if (lookup->matched < 0xffffffffffffffff)
- lookup->matched++;
- if (depth >=3 && lookup->matched > ((struct st_item *)hmap[hkey].list.next)->matched) {
- // simply reorder the list by matched count, except the hmap array head
- list_del(&lookup->list);
- __list_add(&lookup->list, &hmap[hkey].list, hmap[hkey].list.next);
- }
- return lookup->st;
- }
- lookup = (struct st_item *)lookup->list.next;
- if (hmax_depth < ++depth)
- hmax_depth = depth;
- } while (lookup != &hmap[hkey]);
- drd_dbg(DEBUG_HASH, "hvalue not found\n");
- return NULL;
- }
- char *set_hmap(u64 *hvalue)
- {
- int hkey = get_hkey(hvalue);
- struct st_item *newItem = NULL;
- bool first_hit = false;
- drd_dbg(DEBUG_HASH, "hvalue {%d}: <%llx> %llx\n", hkey, *hvalue, hmap[hkey].p);
- if (hmap[hkey].p == 0) {
- newItem = &hmap[hkey];
- first_hit = true;
- } else {
- newItem = kmalloc(sizeof(struct st_item), GFP_ATOMIC);
- if (newItem == NULL) {
- drd_dbg(DEBUG_HASH, "fail to alloc\n");
- spin_unlock_bh(&hlock);
- return NULL;
- }
- init_st_item(newItem);
- list_add_tail(&newItem->list, &hmap[hkey].list);
- hdup_count++;
- }
- newItem->p = *hvalue;
- hmap_count++;
- spin_unlock_bh(&hlock);
- snprintf(newItem->st, ST_SIZE, "%pS", (void *)*hvalue);
- drd_dbg(DEBUG_HASH, "{%d:%d} <%pK> '%s'\n", hkey, first_hit, hvalue, newItem->st);
- return newItem->st;
- }
- /* use direct call instead of recursive stack trace */
- u64 __stack(int depth)
- {
- u64 *func = NULL;
- switch (depth + ST_START) {
- case 3 :
- func = __builtin_return_address(3);
- break;
- case 4 :
- func = __builtin_return_address(4);
- break;
- case 5 :
- func = __builtin_return_address(5);
- break;
- case 6 :
- func = __builtin_return_address(6);
- break;
- case 7 :
- func = __builtin_return_address(7);
- break;
- case 8 :
- func = __builtin_return_address(8);
- break;
- case 9 :
- func = __builtin_return_address(9);
- break;
- case 10 :
- func = __builtin_return_address(10);
- break;
- case 11 :
- func = __builtin_return_address(11);
- break;
- case 12 :
- func = __builtin_return_address(12);
- break;
- case 13 :
- func = __builtin_return_address(13);
- break;
- case 14 :
- func = __builtin_return_address(14);
- break;
- case 15 :
- func = __builtin_return_address(15);
- break;
- case 16 :
- func = __builtin_return_address(16);
- break;
- case 17 :
- func = __builtin_return_address(17);
- break;
- case 18 :
- func = __builtin_return_address(18);
- break;
- case 19 :
- func = __builtin_return_address(19);
- break;
- case 20 :
- func = __builtin_return_address(20);
- break;
- case 21 :
- func = __builtin_return_address(21);
- break;
- case 22 :
- func = __builtin_return_address(22);
- break;
- case 23 :
- func = __builtin_return_address(23);
- break;
- case 24 :
- func = __builtin_return_address(24);
- break;
- case 25 :
- func = __builtin_return_address(25);
- break;
- default :
- return 0;
- }
- return (u64)func;
- }
- #define NOT_TRACE (0xDD)
- #define FIN_TRACE 1
- #define ACK_TRACE 2
- #define GET_TRACE 3
- int chk_stack(char *pos, int net_pkt)
- {
- /* stop tracing */
- if (!strncmp(pos + 4, "f_nbu", 5))// __qdf_nbuf_free
- return NOT_TRACE;
- if (!strncmp(pos, "unix", 4)) // unix_xxx
- return NOT_TRACE;
- if (!strncmp(pos + 2, "tlin", 4)) // netlink_xxx
- return NOT_TRACE;
- if (!strncmp(pos, "tpac", 4)) // tpacket_rcv
- return NOT_TRACE;
- if (!strncmp(pos, "drd", 3)) // drd_xxx
- return NOT_TRACE;
- if (!strncmp(pos + 1, "_sk_d", 5))// __sk_destruct
- return NOT_TRACE;
- #ifdef EXTENDED_DROPDUMP
- /* ignore normally consumed packets on TX path */
- if (!strncmp(pos + 2, "it_on", 5))// xmit_one
- return NOT_TRACE;
- if (!strncmp(pos + 2, "t_tx_", 5))// net_tx_action
- return NOT_TRACE;
- if (!strncmp(pos, "dp_tx", 5)) //dp_tx_comp_xxx
- return NOT_TRACE;
- /* prevent recursive call by __kfree_skb() */
- if (!strncmp(pos + 4, "ree_s", 5))// __kfree_skb
- return NOT_TRACE;
- #endif
- /* end of callstack */
- if (!strncmp(pos, "loc", 3))// local_*
- return FIN_TRACE;
- if (!strncmp(pos + 7, "ftir", 4))// __do_softirq
- return FIN_TRACE;
- if (!strncmp(pos + 7, "rk_r", 4))// task_work_run
- return FIN_TRACE;
- if (!strncmp(pos, "SyS", 3)) // SyS_xxx
- return FIN_TRACE;
- if (!strncmp(pos, "ret_", 4)) // ret_from_xxx
- return FIN_TRACE;
- if (!strncmp(pos, "el", 2)) // el*
- return FIN_TRACE;
- if (!strncmp(pos, "gic", 3)) // gic_xxx
- return FIN_TRACE;
- if (!strncmp(pos + 3, "rt_ke", 5))// start_kernel
- return FIN_TRACE;
- if (!strncmp(pos + 13, "rt_ke", 5))// secondary_start_kernel
- return FIN_TRACE;
- /* network pkt */
- if (!net_pkt) {
- if (!strncmp(pos, "net", 3))
- return GET_TRACE;
- if (!strncmp(pos, "tcp", 3)) {
- // packet from tcp_drop() could be normal operation.
- // don't logging pure ack.
- if (!strncmp(pos, "tcp_drop", 8))
- return ACK_TRACE;
- return GET_TRACE;
- }
- if (!strncmp(pos, "ip", 2))
- return GET_TRACE;
- if (!strncmp(pos, "icmp", 4))
- return GET_TRACE;
- if (!strncmp(pos, "xfr", 3))
- return GET_TRACE;
- }
- return 0;
- }
- static bool _is_tcp_ack(struct sk_buff *skb)
- {
- switch (skb->protocol) {
- /* TCPv4 ACKs */
- case htons(ETH_P_IP):
- if ((ip_hdr(skb)->protocol == IPPROTO_TCP) &&
- (ntohs(ip_hdr(skb)->tot_len) - (ip_hdr(skb)->ihl << 2) ==
- tcp_hdr(skb)->doff << 2) &&
- ((tcp_flag_word(tcp_hdr(skb)) &
- cpu_to_be32(0x00FF0000)) == TCP_FLAG_ACK))
- return true;
- break;
- /* TCPv6 ACKs */
- case htons(ETH_P_IPV6):
- if ((ipv6_hdr(skb)->nexthdr == IPPROTO_TCP) &&
- (ntohs(ipv6_hdr(skb)->payload_len) ==
- (tcp_hdr(skb)->doff) << 2) &&
- ((tcp_flag_word(tcp_hdr(skb)) &
- cpu_to_be32(0x00FF0000)) == TCP_FLAG_ACK))
- return true;
- break;
- }
- return false;
- }
- static inline bool is_tcp_ack(struct sk_buff *skb)
- {
- if (skb_is_tcp_pure_ack(skb))
- return true;
- if (unlikely(_is_tcp_ack(skb)))
- return true;
- return false;
- }
- int symbol_lookup(u64 *addr, int net_pkt) {
- char *symbol = NULL;
- spin_lock_bh(&hlock);
- symbol = get_hmap(addr);
- if (symbol != NULL)
- spin_unlock_bh(&hlock);
- else
- symbol = set_hmap(addr);
- memcpy((char *)addr, symbol, strlen(symbol));
- return chk_stack(symbol, net_pkt);
- }
- u8 get_stack(struct sk_buff *skb, struct sk_buff *dmy, unsigned int offset, unsigned int reason)
- {
- u8 depth = 0, max_depth = ST_MAX;
- struct _dmy_info *dmy_info = (struct _dmy_info *)(dmy->data + offset);
- u64 *stack_base = &dmy_info->stack;
- #ifdef DRD_WQ
- // sometimes __builtin_return_address() returns invalid address for deep stack of
- // ksoftirq or kworker, and migration task. limit the maximun depth for them.
- if ((current->comm[0] == 'k' && (current->comm[4] == 't' || current->comm[4] == 'k')) ||
- (current->comm[0] == 'm' && current->comm[4] == 'a')) {
- dmy_info->flag |= LIMIT_DEPTH_BIT;
- max_depth >>= 1;
- }
- #else
- int chk = 0, net_pkt = 0;
- #endif
- if (skb->tstamp >> 48 < 5000) {
- // packet has kernel timestamp, not utc.
- // using a zero-value for updating to utc at tpacket_rcv()
- dmy_info->flag |= UPDATE_TIME_BIT;
- dmy->tstamp = 0;
- } else {
- // using utc of original packet
- dmy->tstamp = skb->tstamp;
- }
- drd_dbg(DEBUG_SAVE, "trace <%pK>\n", skb);
- for (depth = 0; depth < max_depth; depth++) {
- *stack_base = __stack(depth);
- #ifdef DRD_WQ
- drd_dbg(DEBUG_SAVE, "%02d: <%pK>\n", depth, (u64 *)*stack_base);
- if (*stack_base == 0) {
- // __builtin_return_address() returned root stack
- depth--;
- break;
- }
- #else
- /* functions that instead of when set_stack_work not used */
- chk = symbol_lookup(stack_base, net_pkt);
- drd_dbg(DEBUG_SAVE, "[%2d:%d] <%s>\n", depth, chk, (char *)stack_base);
- if (chk == NOT_TRACE) {
- drd_dbg(DEBUG_TRACE, "not target stack\n");
- return NOT_TRACE;
- }
- if (chk == FIN_TRACE)
- break;
- if (chk == ACK_TRACE) {
- if (is_tcp_ack(skb)) {
- drd_dbg(DEBUG_TRACE, "don't trace tcp ack\n");
- return NOT_TRACE;
- } else {
- net_pkt = 1;
- }
- }
- if (chk == GET_TRACE)
- net_pkt = 1;
- #endif
- stack_base += (ST_SIZE / sizeof(u64));
- }
- memcpy(dmy_info->magic, "DRD", 3);
- dmy_info->depth = depth;
- if (skip_count > 0) {
- dmy_info->skip_count = skip_count;
- skip_count = 0;
- }
- dmy_info->count = ++dropped_count;
- dmy_info->reason_id = reason;
- if (reason < DRD_REASON_MAX) {
- memcpy(dmy_info->reason_str, drd_reasons[reason], min(16, (int)strlen(drd_reasons[reason])));
- } else {
- memcpy(dmy_info->reason_str, "UNDEFINED_REASON", 16);
- }
- drd_dbg(DEBUG_RAW, "<%pK:%pK> %*ph\n", dmy, dmy_info, 16, dmy_info);
- return depth;
- }
- int set_stack_work(struct sk_buff *skb, struct _dmy_info *dmy_info)
- {
- int chk = 0, net_pkt = 0;
- u8 depth;
- u64 *stack_base;
- drd_dbg(DEBUG_RAW, "<%pK:%pK> %*ph\n", skb, dmy_info, 16, dmy_info);
- if (strncmp(dmy_info->magic, "DRD", 3)) {
- drd_dbg(DEBUG_TRACE, "invalid magic <%pK>\n", skb);
- return -1;
- }
- stack_base = &dmy_info->stack;
- for (depth = 0; depth < dmy_info->depth; depth++) {
- chk = symbol_lookup(stack_base, net_pkt);
- drd_dbg(DEBUG_RESTORE, "[%2d:%d] <%s>\n", depth, chk, (char *)stack_base);
- if (chk == NOT_TRACE) {
- drd_dbg(DEBUG_TRACE, "not target stack\n");
- return NOT_TRACE;
- }
- if (chk == FIN_TRACE)
- break;
- if (chk == ACK_TRACE) {
- if (is_tcp_ack(skb)) {
- drd_dbg(DEBUG_TRACE, "don't trace tcp ack\n");
- return NOT_TRACE;
- } else {
- net_pkt = 1;
- }
- }
- if (chk == GET_TRACE)
- net_pkt = 1;
- stack_base += (ST_SIZE / sizeof(u64));
- }
- if (net_pkt == 0) {
- drd_dbg(DEBUG_TRACE, "not defined packet\n");
- return -3;
- }
- return depth;
- }
- #ifdef DRD_WQ
- static void save_pkt_work(struct work_struct *ws)
- {
- struct sk_buff *skb, *next;
- struct packet_type *ptype = NULL;
- struct _dmy_info *dmy_info = NULL;
- int st_depth = 0;
- u16 budget = 0;
- list_for_each_entry_safe(skb, next, &drd_worker.list, list) {
- spin_lock_bh(&drd_worker.lock);
- if (support_dropdump) {
- list_for_each_entry_rcu(ptype, &ptype_log, list) {
- if (ptype != NULL)
- break;
- }
- drd_dbg(DEBUG_LOG, "del %u:%llu <%llx>\n", budget, drd_worker.num, (u64)(skb));
- skb_list_del_init(skb);
- drd_worker.num--;
- } else {
- spin_unlock_bh(&drd_worker.lock);
- return;
- }
- spin_unlock_bh(&drd_worker.lock);
- if (ptype == NULL || list_empty(&ptype->list)) {
- drd_dbg(DEBUG_LOG,"pt list not ready\n");
- __kfree_skb(skb);
- continue;
- }
- dmy_info = (struct _dmy_info *)(skb->data + PKTINFO_OFFSET(skb));
- st_depth = set_stack_work(skb, dmy_info);
- if (st_depth != NOT_TRACE) {
- ptype->func(skb, skb->dev, ptype, skb->dev);
- } else {
- __kfree_skb(skb);
- }
- if (++budget >= budget_limit)
- break;
- }
- if (!list_empty(&drd_worker.list)) {
- if (budget_limit < BUDGET_MAX)
- budget_limit <<= 1;
- queue_delayed_work(drd_worker.wq, &drd_worker.dwork, msecs_to_jiffies(1));
- drd_dbg(DEBUG_LOG, "pkt remained(%llu), trigger again. budget:%d\n", drd_worker.num, budget_limit);
- } else {
- drd_worker.num = 0;
- }
- return;
- }
- #else
- void save_pkt(struct sk_buff *skb)
- {
- struct packet_type *ptype = NULL;
- rcu_read_lock();
- list_for_each_entry_rcu(ptype, &ptype_log, list) {
- if (ptype != NULL)
- break;
- }
- if (ptype == NULL || list_empty(&ptype->list)) {
- drd_dbg(DEBUG_LOG,"pt list not ready\n");
- __kfree_skb(skb);
- goto out;
- }
- drd_dbg(DEBUG_LOG, "%llu <%llx>\n", dropped_count, (u64)(skb));
- ptype->func(skb, skb->dev, ptype, skb->dev);
- out:
- rcu_read_unlock();
- }
- #endif
- int skb_validate(struct sk_buff *skb)
- {
- if (virt_addr_valid(skb) && virt_addr_valid(skb->dev)) {
- struct iphdr *ip4hdr = (struct iphdr *)skb_network_header(skb);
- if (skb->protocol != htons(ETH_P_IPV6)
- && skb->protocol != htons(ETH_P_IP))
- return -1;
- switch (skb->dev->name[0]) {
- case 'r': // rmnet*
- case 'v': // v4-rmnet*
- case 't': // tun
- case 'e': // epdg
- break;
- case 'l': // lo
- case 'b': // bt*
- case 'w': // wlan
- case 's': // swlan
- if (__ratelimit(&drd_ratelimit_pkt))
- break;
- if (skip_count < 0xffff)
- skip_count++;
- dropped_count++;
- return -9;
- default:
- drd_dbg(DEBUG_LOG, "invalid dev: %s\n", skb->dev->name);
- return -2;
- }
- if (unlikely((ip4hdr->version != 4 && ip4hdr->version != 6)
- || ip4hdr->id == 0x6b6b))
- return -3;
- if (unlikely(!skb->len))
- return -4;
- if (unlikely(skb->len > skb->tail))
- return -5;
- if (unlikely(skb->data <= skb->head))
- return -6;
- if (unlikely(skb->tail > skb->end))
- return -7;
- if (unlikely(skb->pkt_type == PACKET_LOOPBACK))
- return -8;
- drd_dbg(DEBUG_RAW, "ndev: %s\n", skb->dev->name);
- return 0;
- }
- return -255;
- }
- struct sk_buff *get_dummy(struct sk_buff *skb, unsigned int reason)//, char *pos, int st_depth)
- {
- struct sk_buff *dummy = NULL;
- struct skb_shared_info *shinfo;
- unsigned int copy_len = PKTINFO_COPYLEN_MAX;
- unsigned int copy_buf_len = PKTINFO_COPYLEN_MAX;
- unsigned int org_len, dummy_len;
- u8 ret = 0;
- struct iphdr *ip4hdr = (struct iphdr *)(skb_network_header(skb));
- struct ipv6hdr *ip6hdr;
- if (ip4hdr->version == 4) {
- org_len = ntohs(ip4hdr->tot_len);
- } else {
- ip6hdr = (struct ipv6hdr *)ip4hdr;
- org_len = skb_network_header_len(skb) + ntohs(ip6hdr->payload_len);
- }
- if (org_len < PKTINFO_COPYLEN_MAX) {
- copy_len = org_len;
- copy_buf_len = round_up(org_len, 0x10);
- }
- dummy_len = copy_buf_len + sizeof(struct _dmy_info) + ST_BUF_SIZE;
- dummy = alloc_skb(dummy_len, GFP_ATOMIC);
- if (unlikely(!dummy)) {
- drd_dbg(DEBUG_LOG, "alloc fail, %u\n", dummy_len);
- return NULL;
- }
- drd_dbg(DEBUG_SAVE, "skb->len:%u org_len:%u copy_len:%u copy_buf_len:%u dummy_len:%u\n",
- skb->len, org_len, copy_len, copy_buf_len, dummy_len);
- dummy->dev = skb->dev;
- dummy->protocol = skb->protocol;
- dummy->ip_summed = CHECKSUM_UNNECESSARY;
- refcount_set(&skb->users, 1);
- skb_put(dummy, dummy_len);
- skb_reset_mac_header(dummy);
- skb_reset_network_header(dummy);
- skb_set_transport_header(dummy, skb_network_header_len(skb));
- shinfo = skb_shinfo(dummy);
- memset(shinfo, 0, sizeof(struct skb_shared_info));
- atomic_set(&shinfo->dataref, 1);
- INIT_LIST_HEAD(&dummy->list);
- memcpy(dummy->data, skb_network_header(skb), copy_len);
- memset((void *)((u64)dummy->data + (u64)copy_len), 0,
- 0x10 - (copy_len % 0x10) + sizeof(struct _dmy_info) + ST_BUF_SIZE);
- ret = get_stack(skb, dummy, copy_buf_len, reason);
- if (ret != NOT_TRACE) {
- PKTINFO_OFFSET(dummy) = copy_buf_len;
- } else {
- drd_dbg(DEBUG_SAVE, "not saving pkt\n");
- __kfree_skb(dummy);
- return NULL;
- }
- return dummy;
- }
- void drd_kfree_skb(struct sk_buff *skb, unsigned int reason)
- {
- struct sk_buff *dmy;
- #ifdef DRD_WQ
- struct sk_buff *next;
- #endif
- if (support_dropdump < 2) {
- #ifdef DRD_WQ
- if (drd_worker.num) {
- drd_dbg(DEBUG_LOG, "purge drd list\n");
- cancel_delayed_work(&drd_worker.dwork);
- spin_lock_bh(&drd_worker.lock);
- list_for_each_entry_safe(dmy, next, &drd_worker.list, list) {
- skb_list_del_init(dmy);
- __kfree_skb(dmy);
- }
- drd_worker.num = 0;
- spin_unlock_bh(&drd_worker.lock);
- }
- #endif
- return;
- }
- if (skb_validate(skb))
- return;
- #ifdef DRD_WQ
- if (unlikely(drd_worker.num >= LIST_MAX - 1)) {
- drd_dbg(DEBUG_LOG, "drd list full\n");
- return;
- }
- #endif
- dmy = get_dummy(skb, reason);
- if (dmy == NULL)
- return;
- #ifdef DRD_WQ
- spin_lock_bh(&drd_worker.lock);
- if (support_dropdump) {
- list_add_tail(&dmy->list, &drd_worker.list);
- drd_worker.num++;
- drd_dbg(DEBUG_LOG, "add %llu <%pK>\n", drd_worker.num, dmy);
- }
- spin_unlock_bh(&drd_worker.lock);
- budget_limit = budget_default;
- queue_delayed_work(drd_worker.wq, &drd_worker.dwork, 0);
- #else
- save_pkt(dmy);
- #endif
- }
- EXPORT_SYMBOL_GPL(drd_kfree_skb);
- void drd_ptype_head(const struct packet_type *pt, struct list_head *vendor_pt)
- {
- if (pt->type == htons(ETH_P_LOG))
- vendor_pt->next = &ptype_log;
- }
- EXPORT_SYMBOL_GPL(drd_ptype_head);
- #if defined(CONFIG_ANDROID_VENDOR_HOOKS)
- static void drd_ptype_head_handler(void *data, const struct packet_type *pt, struct list_head *vendor_pt)
- {
- drd_ptype_head(pt, vendor_pt);
- }
- #else
- /* can't use macro directing the drd_xxx functions instead of lapper. *
- * because of have to use EXPORT_SYMBOL macro for module parts. *
- * it should to be used at here with it's definition */
- void trace_android_vh_ptype_head(const struct packet_type *pt, struct list_head *vendor_pt)
- {
- drd_ptype_head(pt, vendor_pt);
- }
- EXPORT_SYMBOL_GPL(trace_android_vh_ptype_head);
- #endif
- #if defined(TRACE_SKB_DROP_REASON) || defined(DEFINE_DROP_REASON)
- static void drd_kfree_skb_handler(void *data, struct sk_buff *skb,
- void *location, enum skb_drop_reason reason)
- {
- #else
- static void drd_kfree_skb_handler(void *data, struct sk_buff *skb, void *location)
- {
- unsigned int reason = 0;
- #endif
- drd_kfree_skb(skb, (unsigned int)reason);
- }
- struct kobject *drd_kobj;
- int get_attr_input(const char *buf, int *val)
- {
- int ival;
- int err = kstrtoint(buf, 0, &ival);
- if (err >= 0)
- *val = ival;
- else
- drd_info("invalid input: %s\n", buf);
- return err;
- }
- static ssize_t hstat_show(struct kobject *kobj,
- struct kobj_attribute *attr, char *buf)
- {
- return sprintf(buf,
- "stack : total %d, used %lld, dupplicated %llu, max_depth %u, dropped %llu\n",
- DRD_HSIZE, hmap_count, hdup_count, hmax_depth, dropped_count);
- }
- static struct kobj_attribute hstat_attribute = {
- .attr = {.name = "hstat", .mode = 0660},
- .show = hstat_show,
- };
- static ssize_t hmap_show(struct kobject *kobj,
- struct kobj_attribute *attr, char *buf)
- {
- int i;
- struct st_item *lookup;
- for (i = 0; i < DRD_HSIZE; i++) {
- lookup = &hmap[i];
- drd_info("---------------------------------------------------------------------\n");
- do {
- drd_info("%03d <%llx:%llu> '%s'\n", i, lookup->p, lookup->matched, lookup->st);
- lookup = (struct st_item *)lookup->list.next;
- } while (lookup != &hmap[i]);
- }
- drd_info("---------------------------------------------------------------------\n");
- return sprintf(buf, "hmap checked\n");
- }
- static struct kobj_attribute hmap_attribute = {
- .attr = {.name = "hmap", .mode = 0660},
- .show = hmap_show,
- };
- static ssize_t debug_drd_show(struct kobject *kobj,
- struct kobj_attribute *attr, char *buf)
- {
- return sprintf(buf, "current debug_drd: %d (0x%x)\n", debug_drd, debug_drd);
- }
- ssize_t debug_drd_store(struct kobject *kobj, struct kobj_attribute *attr,
- const char *buf, size_t count)
- {
- if (get_attr_input(buf, &debug_drd) >= 0)
- drd_info("debug_drd = %d\n", debug_drd);
- return count;
- }
- static struct kobj_attribute debug_drd_attribute = {
- .attr = {.name = "debug_drd", .mode = 0660},
- .show = debug_drd_show,
- .store = debug_drd_store,
- };
- #ifdef DRD_WQ
- static ssize_t budget_default_show(struct kobject *kobj,
- struct kobj_attribute *attr, char *buf)
- {
- return sprintf(buf, "current budget_default: %u\n", budget_default);
- }
- ssize_t budget_default_store(struct kobject *kobj, struct kobj_attribute *attr,
- const char *buf, size_t count)
- {
- if (get_attr_input(buf, &budget_default) >= 0)
- drd_info("budget_default = %u\n", budget_default);
- return count;
- }
- static struct kobj_attribute budget_default_attribute = {
- .attr = {.name = "budget_default", .mode = 0660},
- .show = budget_default_show,
- .store = budget_default_store,
- };
- #endif
- static struct attribute *dropdump_attrs[] = {
- &hstat_attribute.attr,
- &hmap_attribute.attr,
- &debug_drd_attribute.attr,
- #ifdef DRD_WQ
- &budget_default_attribute.attr,
- #endif
- NULL,
- };
- ATTRIBUTE_GROUPS(dropdump);
- static struct ctl_table drd_proc_table[] = {
- {
- .procname = "support_dropdump",
- .data = &support_dropdump,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec,
- },
- #ifdef EXTENDED_DROPDUMP
- {
- .procname = "support_dropdump_ext",
- .data = &support_dropdump,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec,
- },
- #endif
- { }
- };
- static int __init init_net_drop_dump(void)
- {
- int rc = 0, i;
- drd_info("\n");
- INIT_LIST_HEAD(&ptype_log);
- init_net.core.sysctl_hdr = register_net_sysctl(&init_net, "net/core", drd_proc_table);
- if (init_net.core.sysctl_hdr == NULL) {
- drd_info("init sysctrl failed\n");
- return -ENODEV;
- }
- #if defined(CONFIG_ANDROID_VENDOR_HOOKS)
- rc = register_trace_android_vh_ptype_head(drd_ptype_head_handler, NULL);
- #endif
- rc += register_trace_kfree_skb(drd_kfree_skb_handler, NULL);
- if (rc) {
- drd_info("fail to register android_trace\n");
- return -EIO;
- }
- #ifdef DRD_WQ
- drd_worker.wq = create_workqueue("drd_work");
- if (!drd_worker.wq) {
- drd_info("fail to create wq\n");
- return -ENOMEM;
- }
- INIT_DELAYED_WORK(&drd_worker.dwork, save_pkt_work);
- INIT_LIST_HEAD(&drd_worker.list);
- spin_lock_init(&drd_worker.lock);
- drd_worker.num = 0;
- #endif
- drd_kobj = kobject_create_and_add("dropdump", kernel_kobj);
- if (!drd_kobj) {
- drd_info("fail to create kobj\n");
- rc = -ENOMEM;
- goto kobj_error;
- }
- rc = sysfs_create_groups(drd_kobj, dropdump_groups);
- if (rc) {
- drd_info("fail to create attr\n");
- goto attr_error;
- }
- for (i = 0; i < DRD_HSIZE; i++) {
- init_st_item(&hmap[i]);
- }
- spin_lock_init(&hlock);
- support_dropdump = 0;
- goto out;
- attr_error:
- kobject_put(drd_kobj);
- kobj_error:
- #ifdef DRD_WQ
- destroy_workqueue(drd_worker.wq);
- #endif
- out:
- return rc;
- }
- static void exit_net_drop_dump(void)
- {
- drd_info("\n");
- support_dropdump = 0;
- }
- module_init(init_net_drop_dump);
- module_exit(exit_net_drop_dump);
- MODULE_LICENSE("GPL");
- MODULE_DESCRIPTION("Samsung dropdump module");
|