{"id":1952,"date":"2016-01-28T00:20:34","date_gmt":"2016-01-28T08:20:34","guid":{"rendered":"https:\/\/www.privateinternetaccess.com\/blog\/?p=1952"},"modified":"2024-02-01T01:21:48","modified_gmt":"2024-02-01T09:21:48","slug":"linux-networking-stack-from-the-ground-up-part-3","status":"publish","type":"post","link":"https:\/\/www.privateinternetaccess.com\/blog\/linux-networking-stack-from-the-ground-up-part-3\/","title":{"rendered":"Linux networking stack from the ground up, part 3"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/www.privateinternetaccess.com\/blog\/linux-networking-stack-from-the-ground-up-part-1\/\">part 1<\/a> | <a href=\"https:\/\/www.privateinternetaccess.com\/blog\/linux-networking-stack-from-the-ground-up-part-2\/\">part 2<\/a> | <a href=\"https:\/\/www.privateinternetaccess.com\/blog\/linux-networking-stack-from-the-ground-up-part-3\/\">part 3<\/a> | <a href=\"https:\/\/www.privateinternetaccess.com\/blog\/linux-networking-stack-from-the-ground-up-part-4\/\">part 4<\/a> | <a href=\"https:\/\/www.privateinternetaccess.com\/blog\/linux-networking-stack-from-the-ground-up-part-4-2\/\">part 5<\/a><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Overview<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">This post will pick up where part 2 left off, beginning by describing a packet arriving, examining softirqs, and examining how the <code>e1000e<\/code> driver passes packets up the network stack.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">A packet arrives!<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">So, at long last a packet arrives from the network. Assuming, the rx ring buffer has enough space, the packet is written into the ring buffer via DMA and the device raises the interrupt that is assigned to it (or in the case of MSI-X, the IRQ tied to the rx queue the packet arrived on).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">You can find statistics about hardware interrupts by checking the <code>\/proc\/interrupts<\/code> file.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In general the interrupt handler that runs when an interrupt is raised should try to defer as much processing as possible to happen outside the interrupt context. This is crucial because while an interrupt is being processed, other interrupts are blocked.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If we examine the <code>e1000_intr_msi<\/code> function in e1000e, we can see after some device specific code and workarounds for hardware bugs that the interrupt handler signals NAPI (<a href=\"https:\/\/github.com\/torvalds\/linux\/blob\/v3.13\/drivers\/net\/ethernet\/intel\/e1000e\/netdev.c#L1777-L1783\">drivers\/net\/ethernet\/intel\/e1000e\/netdev.c:1777<\/a>):<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">if (napi_schedule_prep(&amp;adapter-&gt;napi)) {\n  adapter-&gt;total_tx_bytes = 0;\n  adapter-&gt;total_tx_packets = 0;\n  adapter-&gt;total_rx_bytes = 0;\n  adapter-&gt;total_rx_packets = 0;\n  __napi_schedule(&amp;adapter-&gt;napi);\n}<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This code is checking if NAPI is already running and if not, statistics structures are reset and NAPI is scheduled to run to process the packets.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">At a high level, NAPI is scheduled to run from the hardware interrupt handler, but the NAPI code which does the packet processing is run outside of the hardware interrupt context. This is accomplished with softirqs, which will be detailed next.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The <code>__napi_schedule<\/code> function:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">\/**\n * __napi_schedule - schedule for receive\n * @n: entry to schedule\n *\n * The entry's receive function will be scheduled to run\n *\/\nvoid __napi_schedule(struct napi_struct *n)\n{\n  unsigned long flags;\n\n  local_irq_save(flags);\n  ____napi_schedule(&amp;__get_cpu_var(softnet_data), n);\n  local_irq_restore(flags);\n}\nEXPORT_SYMBOL(__napi_schedule);<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Schedules the NAPI poll function to be run. It does this by obtaining the current CPU\u2019s <code>softnet_data<\/code> structure and passing that and the driver provided <code>napi_struct<\/code> to <code>____napi_schedule<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">\/* Called with irq disabled *\/                                                            \nstatic inline void ____napi_schedule(struct softnet_data *sd,                             \n                                     struct napi_struct *napi)                            \n{                                                                                         \n  list_add_tail(&amp;napi-&gt;poll_list, &amp;sd-&gt;poll_list);                                  \n  __raise_softirq_irqoff(NET_RX_SOFTIRQ);                                           \n}<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Which adds the driver provided NAPI poll structure to the <code>softnet_data<\/code> list for the current CPU.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Next, the function calls <code>__raise_softirq_irqoff<\/code> (from <code>kernel\/softirq.c<\/code>) which contains this code:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">struct task_struct *tsk = __this_cpu_read(ksoftirqd);                             \n                                                                       \nif (tsk &amp;&amp; tsk-&gt;state != TASK_RUNNING)                                            \n  wake_up_process(tsk);<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">which brings up an important point: the hardware interrupt handler wakes up the NAPI softirq process on the same CPU as the hardware interrupt handler.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">softirq<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">softirq is one mechanism for executing code outside of the hardware interrupt handler context. As mentioned above, this is important because only minimal work should be done in a hardware interrupt handler; the heavy-lifting should be left for later processing.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The softirq system is a series of kernel threads, one per CPU, that run handler functions that have been registered for different softirqs.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The softirq threads are started early in the kernel initialization process (`kernel\/softirq.c`:754):<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">static __init int spawn_ksoftirqd(void)                                                   \n{       \n        register_cpu_notifier(&amp;cpu_nfb);                                                  \n        \n        BUG_ON(smpboot_register_percpu_thread(&amp;softirq_threads));                         \n        \n        return 0;                                                                         \n}\nearly_initcall(spawn_ksoftirqd);<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The <code>softirq_threads<\/code> structure exports a few fields, but the two important ones are <code>thread_should_run<\/code> and <code>thread_fn<\/code> both of which are called from <code>kernel\/smpboot.c<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The <code>thread_should_run<\/code> function checks for any pending softirqs and if there are one or more, the code in <code>kernel\/smpboot.c<\/code> calls <code>thread_fn<\/code>, which for the softirq system happens to be <code>run_ksoftirqd<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The <code>run_ksoftirqd<\/code> function runs the registered handler function for each softirq that is pending and increments stats that are found in <code>\/proc\/softirqs<\/code>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">NAPI and softirq<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Recall from earlier we saw that the device driver calls <code>__napi_schedule<\/code> which eventually calls <code>__raise_softirq_irqoff(NET_RX_SOFTIRQ);<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This <code>__raise_softirq_irqoff<\/code> function marks the <code>NET_RX_SOFTIRQ<\/code> softirq as pending and wakes up the softirq thread on the current CPU to execute the <code>NET_RX_SOFTIRQ<\/code> handler.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">NET_RX_SOFTIRQ and NET_TX_SOFTIRQ softirq handlers<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Early in the initialization code of the networking subsystem (<a href=\"https:\/\/github.com\/torvalds\/linux\/blob\/v3.13\/net\/core\/dev.c#L6979-L6980\">net\/core\/dev.c:7114<\/a>) we find the following code:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">open_softirq(NET_TX_SOFTIRQ, net_tx_action);\nopen_softirq(NET_RX_SOFTIRQ, net_rx_action);<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">These function calls register the functions <code>net_tx_action<\/code> and <code>net_rx_action<\/code> as softirq handlers that will be run when <code>NET_TX_SOFTIRQ<\/code> and <code>NET_RX_SOFTIRQ<\/code> softirqs are pending.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">rx packet processing begins<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Once the softirq code determines that a softirq is pending, should be processed, and invokes the <code>net_rx_action<\/code> function registered to the <code>NET_RX_SOFTIRQ<\/code>, packet processing begins.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><code>net_rx_action<\/code> processing loop<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><code>net_rx_action<\/code> begins the processing of packets from the memory the packets were DMA\u2019d into by the device driver.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The function iterates through the list of NAPI structures that are queued for the current CPU, dequeuing each structure, one at a time and operating on it.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The processing loop bounds the amount of work and execution time that can be consumed by poll functions. It does this by keeping track of a work \u201cbudget\u201d (which can be adjusted) and checking the elapsed time (<a href=\"https:\/\/github.com\/torvalds\/linux\/blob\/v3.13\/net\/core\/dev.c#L4304-L4309\">net\/core\/dev.c:4366<\/a>):<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">\/* If softirq window is exhuasted then punt.\n * Allow this to run for 2 jiffies since which will allow\n * an average latency of 1.5\/HZ.\n *\/\nif (unlikely(budget &lt;= 0 || time_after_eq(jiffies, time_limit)))\n  goto softnet_break;<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">NAPI also uses a \u201cweight\u201d to prevent individual calls to <code>poll<\/code> from consuming the entire CPU and never terminating. The weight is set on the call to <code>netif_napi_add<\/code> in the device driver initialization; recall that this was hardcoded to 64 in the driver.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The weight is passed into the <code>poll<\/code> function from the driver that is registered to NAPI. This amount dictates the maximum work <code>poll<\/code> can do before it should return. In this case, it can process up to 64 packets.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The <code>poll<\/code> function returns the number of packets processed, which may be any value less than or equal to the weight. This work done is then subtracted from the budget:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">weight = n-&gt;weight;\n\n\/* This NAPI_STATE_SCHED test is for avoiding a race\n * with netpoll's poll_napi().  Only the entity which\n * obtains the lock and sees NAPI_STATE_SCHED set will\n * actually make the -&gt;poll() call.  Therefore we avoid\n * accidentally calling -&gt;poll() when NAPI is not \n *\/\nwork = 0;\nif (test_bit(NAPI_STATE_SCHED, &amp;n-&gt;state)) {\n        work = n-&gt;poll(n, weight);\n        trace_napi_poll(n);\n}\n\nWARN_ON_ONCE(work &gt; weight);\n\nbudget -= work;\n\nlocal_irq_disable();<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">If the work done was equal to the weight, the NAPI structure is moved to the end of the queue and will be examined again. The loop then begins anew, with the budget and time check.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Thus, you can adjust the number of packets processed during the NAPI poll loop by setting the netdev_budget sysctl:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">sysctl net.core.netdev_budget=600<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">You can obtain detailed statistics of the softirq networking system by examining the file <code>\/proc\/net\/softnet_stat<\/code> which outputs information about the number of packets, the number of drops, and the time squeeze counter which tracks the number of times the budget or time limit were consumed, but more work was available.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">NAPI poll == <code>e1000e_poll<\/code><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">It is up to the device driver to clear packets that were DMA\u2019d into the rx ring buffer. This is accomplished by the <code>poll<\/code> method called in the code sample above, which in the case of e1000e, is actually a function pointer to the function <code>e1000e_poll<\/code> (<a href=\"https:\/\/github.com\/torvalds\/linux\/blob\/v3.13\/drivers\/net\/ethernet\/intel\/e1000e\/netdev.c#L2638\">drivers\/net\/ethernet\/intel\/e1000e\/netdev.c:2638<\/a>).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The e1000e driver\u2019s <code>e1000e_poll<\/code> function calls a function via a function pointer named <code>clean_rx<\/code>. It is provided the weight (which was hardcoded to 64 during driver initialization), and a location to write the amount of work done (<a>drivers\/net\/ethernet\/intel\/e1000e\/netdev.c:2638<\/a>):<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">adapter-&gt;clean_rx(adapter-&gt;rx_ring, &amp;work_done, weight);<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This function pointer is set in <code>e1000_open<\/code> when the driver is initialized and the device is brought up. It is set to the an appropriate function based on the MTU.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For our purposes, this is the <code>e1000_clean_rx_irq<\/code> function.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><code>e1000_clean_rx_irq<\/code> \u2013 unmap DMA regions and pass data up the stack<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The <code>e1000_clean_rx_irq<\/code> function runs in a loop, and breaks out when the work done reaches the weight passed into the function.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The function unmaps memory regions that the device has DMA\u2019d data to. Those memory regions are unmapped so they cannot be written to by the device.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Stat counters are incremented for <code>total_rx_bytes<\/code> and <code>total_rx_packets<\/code> and some additional memory regions for DMA are added back to the rx ring.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Finally, <code>e1000_receive_skb<\/code> is called to hand the skb up the network stack.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><code>e1000_receive_skb<\/code> \u2014 pass data up the stack<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The function <code>e1000_receive_skb<\/code> starts a chain of function calls that deal with bookkeeping for things like hardware accelerated vlan tagging and generic receive offloading.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The chain of functions calls is:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><code>e1000_receive_skb<\/code> calls <code>napi_gro_receive<\/code><\/li><li><code>napi_gro_receive<\/code> calls <code>napi_skb_finish<\/code><\/li><li><code>napi_skb_finish<\/code> calls <code>netif_receive_skb<\/code><\/li><\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">And from <code>netif_receive_skb<\/code> the heavy lifting starts. Before we can examine what happens here, we first need describe Receive Packet Steering.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>part 1 | part 2 | part 3 | part 4 | part 5 Overview This post will pick up where part 2 left off, beginning by describing a packet arriving, examining softirqs, and examining how the e1000e driver passes packets up the network stack. A packet arrives! So, at long last a packet arrives &hellip; <a href=\"https:\/\/www.privateinternetaccess.com\/blog\/linux-networking-stack-from-the-ground-up-part-3\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Linux networking stack from the ground up, part 3&#8221;<\/span><\/a><\/p>\n","protected":false},"author":9,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_stopmodifiedupdate":false,"_modified_date":"","footnotes":""},"categories":[1],"tags":[],"class_list":["post-1952","post","type-post","status-publish","format-standard","hentry","category-news"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v26.9 (Yoast SEO v26.9) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>Linux networking stack from the ground up, part 3<\/title>\n<meta name=\"description\" content=\"part 1 | part 2 | part 3 | part 4 | part 5 Overview This post will pick up where part 2 left off, beginning by describing a packet arriving, examining\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.privateinternetaccess.com\/blog\/linux-networking-stack-from-the-ground-up-part-3\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Linux networking stack from the ground up, part 3\" \/>\n<meta property=\"og:description\" content=\"part 1 | part 2 | part 3 | part 4 | part 5 Overview This post will pick up where part 2 left off, beginning by describing a packet arriving, examining\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.privateinternetaccess.com\/blog\/linux-networking-stack-from-the-ground-up-part-3\/\" \/>\n<meta property=\"og:site_name\" content=\"PIA\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/privateinternetaccess\/\" \/>\n<meta property=\"article:published_time\" content=\"2016-01-28T08:20:34+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2024-02-01T09:21:48+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.privateinternetaccess.com\/blog\/wp-content\/uploads\/2018\/07\/ogimage.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"630\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"PIA Research\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@buyvpnservice\" \/>\n<meta name=\"twitter:site\" content=\"@buyvpnservice\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"PIA Research\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"7 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.privateinternetaccess.com\/blog\/linux-networking-stack-from-the-ground-up-part-3\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.privateinternetaccess.com\/blog\/linux-networking-stack-from-the-ground-up-part-3\/\"},\"author\":{\"name\":\"PIA Research\",\"@id\":\"https:\/\/www.privateinternetaccess.com\/blog\/#\/schema\/person\/867d81a36eafaf83e91f6528aca0ba29\"},\"headline\":\"Linux networking stack from the ground up, part 3\",\"datePublished\":\"2016-01-28T08:20:34+00:00\",\"dateModified\":\"2024-02-01T09:21:48+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.privateinternetaccess.com\/blog\/linux-networking-stack-from-the-ground-up-part-3\/\"},\"wordCount\":1164,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/www.privateinternetaccess.com\/blog\/#organization\"},\"articleSection\":[\"General Privacy News\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.privateinternetaccess.com\/blog\/linux-networking-stack-from-the-ground-up-part-3\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.privateinternetaccess.com\/blog\/linux-networking-stack-from-the-ground-up-part-3\/\",\"url\":\"https:\/\/www.privateinternetaccess.com\/blog\/linux-networking-stack-from-the-ground-up-part-3\/\",\"name\":\"Linux networking stack from the ground up, part 3\",\"isPartOf\":{\"@id\":\"https:\/\/www.privateinternetaccess.com\/blog\/#website\"},\"datePublished\":\"2016-01-28T08:20:34+00:00\",\"dateModified\":\"2024-02-01T09:21:48+00:00\",\"description\":\"part 1 | part 2 | part 3 | part 4 | part 5 Overview This post will pick up where part 2 left off, beginning by describing a packet arriving, examining\",\"breadcrumb\":{\"@id\":\"https:\/\/www.privateinternetaccess.com\/blog\/linux-networking-stack-from-the-ground-up-part-3\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.privateinternetaccess.com\/blog\/linux-networking-stack-from-the-ground-up-part-3\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.privateinternetaccess.com\/blog\/linux-networking-stack-from-the-ground-up-part-3\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.privateinternetaccess.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Linux networking stack from the ground up, part 3\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.privateinternetaccess.com\/blog\/#website\",\"url\":\"https:\/\/www.privateinternetaccess.com\/blog\/\",\"name\":\"PIA\",\"description\":\"Online privacy news from around the world.\",\"publisher\":{\"@id\":\"https:\/\/www.privateinternetaccess.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.privateinternetaccess.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.privateinternetaccess.com\/blog\/#organization\",\"name\":\"Private Internet Access\",\"url\":\"https:\/\/www.privateinternetaccess.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.privateinternetaccess.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.privateinternetaccess.com\/blog\/wp-content\/uploads\/2018\/07\/pialogowhitekglogo.png\",\"contentUrl\":\"https:\/\/www.privateinternetaccess.com\/blog\/wp-content\/uploads\/2018\/07\/pialogowhitekglogo.png\",\"width\":1200,\"height\":1200,\"caption\":\"Private Internet Access\"},\"image\":{\"@id\":\"https:\/\/www.privateinternetaccess.com\/blog\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/privateinternetaccess\/\",\"https:\/\/x.com\/buyvpnservice\",\"https:\/\/www.instagram.com\/piavpn\/\",\"https:\/\/www.youtube.com\/channel\/UClyJZ47Rizb1xnwuKXDI0_w\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.privateinternetaccess.com\/blog\/#\/schema\/person\/867d81a36eafaf83e91f6528aca0ba29\",\"name\":\"PIA Research\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.privateinternetaccess.com\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/717a02042be28ce22f2ae82923cdaa82551205b8768e3cd4a8fce14988ed0ccd?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/717a02042be28ce22f2ae82923cdaa82551205b8768e3cd4a8fce14988ed0ccd?s=96&d=mm&r=g\",\"caption\":\"PIA Research\"},\"sameAs\":[\"https:\/\/www.privateinternetaccess.com\"],\"url\":\"https:\/\/www.privateinternetaccess.com\/blog\/author\/piaresearch\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Linux networking stack from the ground up, part 3","description":"part 1 | part 2 | part 3 | part 4 | part 5 Overview This post will pick up where part 2 left off, beginning by describing a packet arriving, examining","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.privateinternetaccess.com\/blog\/linux-networking-stack-from-the-ground-up-part-3\/","og_locale":"en_US","og_type":"article","og_title":"Linux networking stack from the ground up, part 3","og_description":"part 1 | part 2 | part 3 | part 4 | part 5 Overview This post will pick up where part 2 left off, beginning by describing a packet arriving, examining","og_url":"https:\/\/www.privateinternetaccess.com\/blog\/linux-networking-stack-from-the-ground-up-part-3\/","og_site_name":"PIA","article_publisher":"https:\/\/www.facebook.com\/privateinternetaccess\/","article_published_time":"2016-01-28T08:20:34+00:00","article_modified_time":"2024-02-01T09:21:48+00:00","og_image":[{"width":1200,"height":630,"url":"https:\/\/www.privateinternetaccess.com\/blog\/wp-content\/uploads\/2018\/07\/ogimage.png","type":"image\/png"}],"author":"PIA Research","twitter_card":"summary_large_image","twitter_creator":"@buyvpnservice","twitter_site":"@buyvpnservice","twitter_misc":{"Written by":"PIA Research","Est. reading time":"7 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.privateinternetaccess.com\/blog\/linux-networking-stack-from-the-ground-up-part-3\/#article","isPartOf":{"@id":"https:\/\/www.privateinternetaccess.com\/blog\/linux-networking-stack-from-the-ground-up-part-3\/"},"author":{"name":"PIA Research","@id":"https:\/\/www.privateinternetaccess.com\/blog\/#\/schema\/person\/867d81a36eafaf83e91f6528aca0ba29"},"headline":"Linux networking stack from the ground up, part 3","datePublished":"2016-01-28T08:20:34+00:00","dateModified":"2024-02-01T09:21:48+00:00","mainEntityOfPage":{"@id":"https:\/\/www.privateinternetaccess.com\/blog\/linux-networking-stack-from-the-ground-up-part-3\/"},"wordCount":1164,"commentCount":0,"publisher":{"@id":"https:\/\/www.privateinternetaccess.com\/blog\/#organization"},"articleSection":["General Privacy News"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.privateinternetaccess.com\/blog\/linux-networking-stack-from-the-ground-up-part-3\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.privateinternetaccess.com\/blog\/linux-networking-stack-from-the-ground-up-part-3\/","url":"https:\/\/www.privateinternetaccess.com\/blog\/linux-networking-stack-from-the-ground-up-part-3\/","name":"Linux networking stack from the ground up, part 3","isPartOf":{"@id":"https:\/\/www.privateinternetaccess.com\/blog\/#website"},"datePublished":"2016-01-28T08:20:34+00:00","dateModified":"2024-02-01T09:21:48+00:00","description":"part 1 | part 2 | part 3 | part 4 | part 5 Overview This post will pick up where part 2 left off, beginning by describing a packet arriving, examining","breadcrumb":{"@id":"https:\/\/www.privateinternetaccess.com\/blog\/linux-networking-stack-from-the-ground-up-part-3\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.privateinternetaccess.com\/blog\/linux-networking-stack-from-the-ground-up-part-3\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.privateinternetaccess.com\/blog\/linux-networking-stack-from-the-ground-up-part-3\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.privateinternetaccess.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Linux networking stack from the ground up, part 3"}]},{"@type":"WebSite","@id":"https:\/\/www.privateinternetaccess.com\/blog\/#website","url":"https:\/\/www.privateinternetaccess.com\/blog\/","name":"PIA","description":"Online privacy news from around the world.","publisher":{"@id":"https:\/\/www.privateinternetaccess.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.privateinternetaccess.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.privateinternetaccess.com\/blog\/#organization","name":"Private Internet Access","url":"https:\/\/www.privateinternetaccess.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.privateinternetaccess.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/www.privateinternetaccess.com\/blog\/wp-content\/uploads\/2018\/07\/pialogowhitekglogo.png","contentUrl":"https:\/\/www.privateinternetaccess.com\/blog\/wp-content\/uploads\/2018\/07\/pialogowhitekglogo.png","width":1200,"height":1200,"caption":"Private Internet Access"},"image":{"@id":"https:\/\/www.privateinternetaccess.com\/blog\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/privateinternetaccess\/","https:\/\/x.com\/buyvpnservice","https:\/\/www.instagram.com\/piavpn\/","https:\/\/www.youtube.com\/channel\/UClyJZ47Rizb1xnwuKXDI0_w"]},{"@type":"Person","@id":"https:\/\/www.privateinternetaccess.com\/blog\/#\/schema\/person\/867d81a36eafaf83e91f6528aca0ba29","name":"PIA Research","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.privateinternetaccess.com\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/717a02042be28ce22f2ae82923cdaa82551205b8768e3cd4a8fce14988ed0ccd?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/717a02042be28ce22f2ae82923cdaa82551205b8768e3cd4a8fce14988ed0ccd?s=96&d=mm&r=g","caption":"PIA Research"},"sameAs":["https:\/\/www.privateinternetaccess.com"],"url":"https:\/\/www.privateinternetaccess.com\/blog\/author\/piaresearch\/"}]}},"_links":{"self":[{"href":"https:\/\/www.privateinternetaccess.com\/blog\/wp-json\/wp\/v2\/posts\/1952","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.privateinternetaccess.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.privateinternetaccess.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.privateinternetaccess.com\/blog\/wp-json\/wp\/v2\/users\/9"}],"replies":[{"embeddable":true,"href":"https:\/\/www.privateinternetaccess.com\/blog\/wp-json\/wp\/v2\/comments?post=1952"}],"version-history":[{"count":8,"href":"https:\/\/www.privateinternetaccess.com\/blog\/wp-json\/wp\/v2\/posts\/1952\/revisions"}],"predecessor-version":[{"id":18957,"href":"https:\/\/www.privateinternetaccess.com\/blog\/wp-json\/wp\/v2\/posts\/1952\/revisions\/18957"}],"wp:attachment":[{"href":"https:\/\/www.privateinternetaccess.com\/blog\/wp-json\/wp\/v2\/media?parent=1952"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.privateinternetaccess.com\/blog\/wp-json\/wp\/v2\/categories?post=1952"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.privateinternetaccess.com\/blog\/wp-json\/wp\/v2\/tags?post=1952"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}