[{"content":"Back in 2021, I published a short post titled Welcome Back. In it, I mentioned that years prior, I had lost my entire original blog located at mellowd.co.uk/ccie during a botched server upgrade. That site had originally chronicled my journey toward my CCIE (#28448) and JNCIE-SP (#1134), along with deep dives into BGP, QoS, MPLS, and protocol internals.\nAt the time of that 2021 post, I managed to manually piece back 13 articles using the Wayback Machine. But life got busy, and more than 20 detailed technical posts, along with dozens of network diagrams, hardware photos, and Wireshark captures, remained trapped in digital limbo.\nToday, that finally changes. The entire archive has been fully restored, converted into clean Markdown, modernized with a brand new theme, and deployed to static edge infrastructure—all while strictly preserving every single historical inbound link.\nHere is a technical look at how I recovered the lost content, hunted down the missing media, and built a bulletproof redirection architecture.\n1. Finding the Lost 2020 Snapshot While performing a comprehensive systems audit of my BGP infrastructure fleet, I discovered an unlinked 2.9 MB static mirror of the old WordPress site sitting at /var/www/html/ccie/ on my primary route reflector node (BGP1).\nIt wasn\u0026rsquo;t a database dump, but rather a wget-style static HTML crawl taken back in May 2020 before the original server was decommissioned. While many links were broken and filenames contained query parameters like ?p=1472 and ?paged=2, the raw HTML of the posts was intact.\nBuilding an Automated HTML-to-Markdown Pipeline Rather than manually copying and pasting dozens of posts, I built a Python extraction pipeline using BeautifulSoup:\nScraping Post Entities: I parsed every HTML document, extracting post titles, original ISO publication timestamps, categories, and tags. Sanitizing WordPress Artifacts: I stripped out legacy Google AdSense tags (\u0026lt;!-- Small top right --\u0026gt;), dead Jetpack sharing widgets, and WordPress styling cruft. Smart Code Block Formatting: Network and systems posts rely heavily on code and CLI captures. My parser inspected \u0026lt;pre\u0026gt; and \u0026lt;code\u0026gt; blocks and automatically assigned appropriate syntax highlighting flags: C code (#include, malloc, struct) → tagged with c Python scripts (import, def, print) → tagged with python Cisco IOS and BIRD configs (show ip route, router bgp, protocol device) → tagged with bash Generating Clean Frontmatter: Each article was output with standard Hugo YAML frontmatter, standardizing metadata across the entire archive. This pipeline recovered 21 brand new posts, instantly expanding the active library from 13 to 34 full technical articles.\n2. The Great Diagram Hunt A networking blog without topology diagrams is nearly useless. Explaining 802.1Q native VLAN behavior, DHCP snooping, or C linked list pointers requires visual context.\nWhile some images survived in the 2020 snapshot, 32 diagrams were missing, including:\nOriginal 2011 home lab hardware photos (building the Dynamips breakout box with Sun Quad Fast Ethernet PCI cards) Wireshark packet captures from the SPAN/RSPAN analysis Call-stack diagrams explaining recursive function execution Route count graphs showing the impact of Hurricane Sandy on the global BGP table Bypassing Archive Rate Limits via Multi-Region Fleet Nodes To recover the missing media, I targeted the Internet Archive Wayback Machine\u0026rsquo;s raw storage endpoints (https://web.archive.org/web/2016id_/\u0026lt;url\u0026gt;). However, making dozens of rapid media requests from a single local IP quickly hit rate limits (HTTP 429 Too Many Requests).\nBecause my infrastructure spans multiple dedicated servers across the US and UK, I distributed the download tasks across clean IPs on the fleet (BGP3 and BGP4). Within minutes, I retrieved all 32 missing diagrams and verified 100% media coverage across all 34 posts—without a single broken image link.\nAll 61 recovered images are now committed directly to Git in /static/images/.\n3. Preserving 15 Years of Inbound Links Tim Berners-Lee famously wrote that \u0026ldquo;Cool URIs don\u0026rsquo;t change\u0026rdquo;. Over the past 15 years, countless forum threads on the Cisco Learning Network, Reddit (r/networking), personal engineering blogs, and Twitter have linked to posts on mellowd.co.uk/ccie/.\nHistorically, WordPress used two URL formats:\nQuery-string permalinks: https://mellowd.co.uk/ccie/?p=5771 Slug-based URLs: https://mellowd.co.uk/ccie/linked-lists/ Under my modern Hugo structure, canonical URLs follow the clean /post/\u0026lt;slug\u0026gt;/ hierarchy (e.g., /post/linked-lists/). If someone clicked a 12-year-old link from a Cisco forum, getting a 404 would be a failure.\nHugo Aliases to the Rescue I leveraged Hugo\u0026rsquo;s aliases feature in every single post\u0026rsquo;s frontmatter:\n--- title: \u0026#34;Using bird to pull global BGP route counts\u0026#34; date: 2014-12-15T21:33:54Z tags: [\u0026#34;awk\u0026#34;, \u0026#34;bgp\u0026#34;, \u0026#34;bird\u0026#34;, \u0026#34;internet\u0026#34;, \u0026#34;linux\u0026#34;, \u0026#34;script\u0026#34;] aliases: - \u0026#34;/ccie/?p=5771\u0026#34; - \u0026#34;/ccie/using-bird-to-pull-global-bgp-route-counts/\u0026#34; --- When Hugo builds the site, it generates lightweight HTML redirect files containing \u0026lt;meta http-equiv=\u0026quot;refresh\u0026quot; content=\u0026quot;0; url=...\u0026quot;\u0026gt; and canonical headers for every alias.\nI generated 151 redirect aliases covering every legacy path, old WordPress post ID, and slug variation across all 34 posts. If you visit /ccie/, /ccie/?p=788, or /ccie/protocol-fundamentals-dot1q/, you are instantly and seamlessly redirected to the correct article.\n4. Modernizing the Infrastructure Beyond recovering content, I also modernized how the site runs:\nDecommissioning Nginx on BGP1: Previously, Nginx ran directly on my primary Route Reflector (BGP1), consuming memory and exposing ports 80 and 443 to internet scanner noise. With the archive safely extracted and converted, I stopped, disabled, and permanently masked nginx.service on BGP1, and removed public HTTP/HTTPS ports from /etc/nftables.conf. BGP1 is now 100% dedicated to BIRD 2 routing.\nStatic Edge Hosting: The site is now built with Hugo v0.167.0 and deployed directly to Google Cloud Storage (gs://mellowd.co.uk), sitting behind Cloudflare CDN. There are no databases, no server runtimes, and no dynamic CMS vulnerabilities. The entire 34-post site compiles in 196 milliseconds.\nFresh Theme — PaperMod: I replaced the old theme with PaperMod, configured with:\nInstant client-side fuzzy search at /search/ Full timeline archive by year at /archives/ Tag taxonomy browsing at /tags/ Automatic dark/light mode switching based on system preferences One-click copy buttons on all code fences Summary of the Restored Archive The full table of contents now includes:\nCategory Articles BGP \u0026amp; Routing Using bird to pull global BGP route counts, Hurricane Sandy\u0026rsquo;s affect on the core BGP table, Creative Routing Contest, Building my topology, RIB, FIB, LFIB, LIB etc Switching \u0026amp; Protocols SPAN, RSPAN, Layer 2 control packets and VLANS, DHCP Snooping – Filter those broadcasts!, Protocol Fundamentals: dot1q, Protocol Fundamentals: ARP, Protocol Fundamentals: Traceroute, Traceroute differences between Windows and Linux QoS \u0026amp; MPLS Junos and IOS QoS (Parts 1–3), Catalyst 3750 QoS EF/BE, MPLS L3VPN: RD vs RT vs VPN Label, Access-lists vs Prefix-lists CS \u0026amp; Programming Linked lists (C), Structs in C, Visualising Big O notation, Visualising recursive functions, Basic OOP Python, Python Multithreading, When and when not to multithread, Python and MySQL, Python paths and Cron logging Lab \u0026amp; Milestones ESXi whitebox server build, 350-001 CCIE Written v4 passed, Cisco Live 2016 – Las Vegas, One Million Views Everything is committed to Git and backed up to GitHub at mellowdrifter/mellowd.co.uk.\nIt feels great to have 15 years of technical notes, lab struggles, and networking history back online, accessible, and permanently preserved. Enjoy reading!\n","permalink":"https://mellowd.co.uk/post/restoring-the-archive-and-preserving-history/","summary":"\u003cp\u003eBack in 2021, I published a short post titled \u003cem\u003eWelcome Back\u003c/em\u003e. In it, I mentioned that years prior, I had lost my entire original blog located at \u003ccode\u003emellowd.co.uk/ccie\u003c/code\u003e during a botched server upgrade. That site had originally chronicled my journey toward my CCIE (#28448) and JNCIE-SP (#1134), along with deep dives into BGP, QoS, MPLS, and protocol internals.\u003c/p\u003e\n\u003cp\u003eAt the time of that 2021 post, I managed to manually piece back 13 articles using the Wayback Machine. But life got busy, and more than 20 detailed technical posts, along with dozens of network diagrams, hardware photos, and Wireshark captures, remained trapped in digital limbo.\u003c/p\u003e","title":"Resurrecting the Blog: How I Restored the Archive and Preserved 15 Years of Links"},{"content":"Website of Darren O\u0026rsquo;Connor. Google Engineer. Networks and Code.\nOriginally documenting my CCIE journey many years ago, this site is slowly being brought back from the dead.\n","permalink":"https://mellowd.co.uk/about/","summary":"\u003cp\u003eWebsite of \u003ca href=\"https://twitter.com/mellowdrifter\"\u003eDarren O\u0026rsquo;Connor\u003c/a\u003e. Google Engineer. Networks and Code.\u003c/p\u003e\n\u003cp\u003eOriginally documenting my CCIE journey many years ago, this site is slowly being brought back from the dead.\u003c/p\u003e","title":"About"},{"content":"A long time ago I had a pretty successful blog located at mellowd.co.uk/ccie - I used this blog at the time to document my journey towards my CCIEs and well as my JNCIE. Unfortunatley, I lost the entire blog in a botched upgrade a few years back. My intention here is to try and republish some of my better posts. I\u0026rsquo;ll be able to extract this info from the wayback machine. Note that a lot of the posts were relevent years ago and things have since moved on. But some of the fundamentals never change :)\nI\u0026rsquo;ll be releasing content over the coming weeks. There is no real timeline.\nUpdate (October 2026): The restoration is complete! I managed to recover the full archive (34 technical posts, all original diagrams, and Wireshark captures), modernize the theme, and preserve 15 years of inbound links. Read the full technical story: Resurrecting the Blog: How I Restored the Archive and Preserved 15 Years of Links.\n","permalink":"https://mellowd.co.uk/post/welcome-back/","summary":"\u003cp\u003eA long time ago I had a pretty successful blog located at mellowd.co.uk/ccie - I used this blog at the time to document my journey towards my CCIEs and well as my JNCIE. Unfortunatley, I lost the entire blog in a botched upgrade a few years back. My intention here is to try and republish some of my better posts. I\u0026rsquo;ll be able to extract this info from the \u003ca href=\"https://archive.org/web/\"\u003ewayback machine\u003c/a\u003e. Note that a lot of the posts were relevent years ago and things have since moved on. But some of the fundamentals never change :)\u003c/p\u003e","title":"Welcome Back"},{"content":"So I’m finally off to Cisco Live! This will be my first time there, and I’m hoping to meet and speak to a lot of people I speak to online.\nSo far my timetable looks like so:\nMonday 11th July Troubleshooting BGP [BRKRST­3320] ASR­9000/IOS­XR Understanding forwarding, troubleshooting the system and XR operations [BRKSPG­2904] Introduction to Segment Routing [BRKRST­2124] A Practical Introduction to DevOps Practices and Tools [BRKCLD­1003] Tuesday 12th July IP LFA (Loop­Free­Alternate): Architecture and Troubleshooting [BRKRST­3020] How to write an IPv6 Addressing Plan [BRKRST­2667] Addressing Networking challenges with latest Innovations in IPv6 [BRKRST­2616] Wednesday 13th July Troubleshooting End­to­End MPLS [BRKMPL­3124] Hitchhiker’s Guide to Troubleshooting IPv6 ­ Advanced [BRKRST­3304] High Availability in the Access [BRKCRS­3438] Thursday 14th July Building a Software Defined Service Provider [BRKSPG­2001] The Future of SP and Your Role in It [INTGEN­1007] Advanced ­ Scaling BGP [BRKRST­3321] That’s it so far. I may change things around, but should be a lot of fun!\n","permalink":"https://mellowd.co.uk/post/cisco-live-2016-las-vegas/","summary":"\u003cp\u003eSo I’m finally off to Cisco Live! This will be my first time there, and I’m hoping to meet and speak to a lot of people I speak to online.\u003c/p\u003e\n\u003cp\u003eSo far my timetable looks like so:\u003c/p\u003e\n\u003ch4 id=\"monday-11th-july\"\u003eMonday 11th July\u003c/h4\u003e\n\u003cul\u003e\n\u003cli\u003eTroubleshooting BGP [BRKRST­3320]\nASR­9000/IOS­XR Understanding forwarding, troubleshooting the system and XR operations [BRKSPG­2904]\nIntroduction to Segment Routing [BRKRST­2124]\nA Practical Introduction to DevOps Practices and Tools [BRKCLD­1003]\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch4 id=\"tuesday-12th-july\"\u003eTuesday 12th July\u003c/h4\u003e\n\u003cul\u003e\n\u003cli\u003eIP LFA (Loop­Free­Alternate): Architecture and Troubleshooting [BRKRST­3020]\nHow to write an IPv6 Addressing Plan [BRKRST­2667]\nAddressing Networking challenges with latest Innovations in IPv6 [BRKRST­2616]\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch4 id=\"wednesday-13th-july\"\u003eWednesday 13th July\u003c/h4\u003e\n\u003cul\u003e\n\u003cli\u003eTroubleshooting End­to­End MPLS [BRKMPL­3124]\nHitchhiker’s Guide to Troubleshooting IPv6 ­ Advanced [BRKRST­3304]\nHigh Availability in the Access [BRKCRS­3438]\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch4 id=\"thursday-14th-july\"\u003eThursday 14th July\u003c/h4\u003e\n\u003cul\u003e\n\u003cli\u003eBuilding a Software Defined Service Provider [BRKSPG­2001]\nThe Future of SP and Your Role in It [INTGEN­1007]\nAdvanced ­ Scaling BGP [BRKRST­3321]\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eThat’s it so far. I may change things around, but should be a lot of fun!\u003c/p\u003e","title":"Cisco Live 2016 – Las Vegas"},{"content":"It’s hard to believe that my blog has just surpassed 1000000 views!\nI started this blog out just on the side to go over things I was learning. I’ve learned a lot in the process, and managed to bag myself two CCIEs, a JNCIE-SP, a job at Google, and the opportunity to write a book for Juniper.\nThere were times I almost completely lost my blog in the past, but I’ve learned how to make good backups. My site has been reachable over native IPv6 for many years, and I moved it all to HTTPS over a year ago.\nRecently there has been a lack of updates. Google keeps me very busy in an enjoyable job, and this is the main reason I struggle to find the time to do updates. I currently have 128 drafts sitting waiting, and I doubt the majority will ever get published :(\nNumbers Views – 1,000,115 Most on a single day – 1,592 Published posts – 322 Drafts – 128 What’s poular All time top post – Access-lists vs Prefix-lists\nSecond all time top post – BGP Confederations – How, What and Why.\n2015 top posts:\nESXi whitebox server\nMPLS L3VPN – Route Distinguisher vs Route Target vs VPN label\nMoving routes between a VRF and the global (default) RIB – Part 1 – Cisco IOS\nIt seems to me that my top posts are usually explaining something simple that a lot of people get wrong. I also tend to get high hits on lab building posts. Oddly, some of my own favourite posts barely get any views at all :(\nI want to thank everyone who has ever visited the site. It’s really weird when people meet me for the first time and know me through my blog. Long may it continue!\nDarren\n","permalink":"https://mellowd.co.uk/post/one-million-views/","summary":"\u003cp\u003eIt’s hard to believe that my blog has just surpassed 1000000 views!\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"Screenshot from 2015-12-04 15:52:03\" loading=\"lazy\" src=\"/images/Screenshot-from-2015-12-04-155203.png\"\u003eI started this blog out just on the side to go over things I was learning. I’ve learned a lot in the process, and managed to bag myself two CCIEs, a JNCIE-SP, a job at Google, and the opportunity to \u003ca href=\"https://www.juniper.net/us/en/training/jnbooks/day-one/networking-technologies-series/mpls-enterprise-engineers/\"\u003ewrite a book for Juniper\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eThere were times I almost completely lost my blog in the past, but I’ve learned how to make good backups. My site has been reachable over native IPv6 for many years, and I moved it all to HTTPS over a year ago.\u003c/p\u003e","title":"One Million Views"},{"content":"I’m currently learning as much computer science as I can on the side. I’ve come across Big O notation a few times already, and while I understand it, I’m much more of a visual guy.\nIt’s rather easy to use Python and matplotlib to graph out how a function’s execution time grows as the size of the input grows. The important things to note is not total execution time, but rather how the runtime of that function grows in relation to the input size. This can be plotted onto a graph which should give us a nice representation of Big O notations.\nNote too that Big O notations always show the worst case. For this reason I’ll ensure to use values where the function will have to do the most work for.\nO(1) O(1) means constant time. No matter what size the input, the runtime will always be the same. A simple example is finding the middle number in a list. I’ll ensure that all code return the amount of time a command was run in the function. This may make the code look just a bit bloated, but for a good reason.\nTo find the center of a list we simply divide the length of the list in two, and return that number. It does not matter if a list has 10 elements or 100 elements, the same amount of steps is performed:\ndef O1(input): count = 0 result = input[len(input) / 2] count += 1 return count I have created 5 lists. The first is length 10, second length 20, and so on. I’ll get the returned values and plot them.\nO(1) plot As can be seen, it doesn’t matter the size of the input. It will always run in the same constant time.\nO(logN) O(logN) increases as the input size goes up. However it goes up as a log of the input size. This means that you can exponentially increase your input size, without linearly increasing the processing time to match.\ndef OlogN(input): def search(length, count): count += 1 length /= 2 if length == 1 or length == 0: return 1 + count else: return 1 + search(length, count) return 1 + search(len(input), 1) O(logN) plot The run time is going up, but look at the size of the inputs at the bottom. I start with 10,000 and move up to 500,000. The number of steps has increased, but not significantly.\nO(N) O(N) is linear. This means that the run time is linearly matched to the input size. They should increase at exactly the same rate.\ndef ON(input, check): count = 0 for number in input: count += 1 if number == check: return 1 + count O(N) plot There is a 1:1 correlation between input size and run time. As expected this produces a linear graph.\nO(N2) O(N2)’s runtime will go up as a square of the input size. The runtime goes up faster than your input sizes, so processing time increases rapidly. This is usually when you iterate through multiple loops at the same time like so:\ndef ON2(input): count = 0 for i in input: count += 1 for j in input: count += 1 return 1 + count O(N2) plot O(N3) O(N3)is merely O(N2) with another exponent. I wanted to show the difference by simply changing the exponent.\ndef ON3(input): count = 0 for i in input: count += 1 for j in input: count += 1 for k in input: count +=1 return 1 + count O(N3) plot Graphs increase rapidly as the exponent increases.\nConclusions I’ve not shown every single type of algorithm, as I just wanted to show the ones I have the most experience with. It’s nice to have a visual representation of these things as it really drills down just how fast your runtime can increase with larger inputs.\nYou can find my code used over here.\n","permalink":"https://mellowd.co.uk/post/visualising-big-o-notation/","summary":"\u003cp\u003eI’m currently learning as much computer science as I can on the side. I’ve come across \u003ca href=\"https://en.wikipedia.org/wiki/Big_O_notation\"\u003eBig O notation\u003c/a\u003e a few times already, and while I understand it, I’m much more of a visual guy.\u003c/p\u003e\n\u003cp\u003eIt’s rather easy to use Python and matplotlib to graph out how a function’s execution time grows as the size of the input grows. The important things to note is not total execution time, but rather how the runtime of that function grows in relation to the input size. This can be plotted onto a graph which should give us a nice representation of Big O notations.\u003c/p\u003e","title":"Visualising Big O notation"},{"content":"Python has a rather handy list method. It allows you to add and remove items at will. How it actually does this is rather elaborate and you can read all about it over here.\nC doesn’t give you the same flexibility. When you create an array, it is of X size. That size cannot change as there is no guarantee that memory on either side of the existing array space is even free for you. For this reason there is a handy data structure called the linked list.\nLinked List A linked list is essentially a struct node with a list item, and then a pointer to the next node. If you need to add an item to a list, you simply create another node in memory. As each item is created, it doesn’t matter where in memory it’s created, as the previous item has a pointer to the new node. A picture really helps here:\nAs we shall see, adding, inserting, and removing items are all very easy to do with linked lists. It’s not all roses though. Linked lists use more memory than an array of similar items. Each node has a pointer to the next item which takes up memory. The bigger issue is random access.\nLet’s say we initialise an array with 1000 items. If I wanted to get to item 500, the computer would simply look at the address of item 0, work out the start address plus 500 of item size, and read that location.\nYou can’t do this with a linked list. Each node is in a separate location. They could be lined up next to each other, but they can just as easily be all over the place. The only way to get to item 500 in a linked list, is to start at node 0, read the pointer to the next node, then keep doing that until you reach node 500.\nHow do you know when you reach the end of the linked list? You could probably use any kind of escape character, but NULL tends to get used.\nBasic implementation So how do we create a linked list? Here I’ll create a struct called node that will hold an integer. The struct will also hold a pointer to the next node.\n#include \u0026lt;stdio.h\u0026gt; // Struct node with pointer to struct node typedef struct node { int number; struct node* next; }node; int main(void) { // Create four nodes node node1; node node2; node node3; node node4; // Place numbers inside the four nodes we made node1.number = 10; node2.number = 20; node3.number = 30; node4.number = 40; // Each node will point to the next node node1.next = \u0026amp;node2; node2.next = \u0026amp;node3; node3.next = \u0026amp;node4; node4.next = NULL; } Here I have created four nodes. Each points to the next node, except for node 4. It has it’s next pointer assigned to NULL. If you fail to assign this NULL value, the pointer will have whatever value what in that memory location when you create it. That is going to point to some random value which is bad news. In order to traverse the list, we need a pointer to the root, or currently node1’s address. This is a pointer that should never change unless node1 is deleted or a node is inserted before it. We also need a cursor that will be updated with the current node’s address. I’ll create a function that prints out items in the list. The following is added to main()\n// Never lose the root position, otherwise you // lose access to your list! node* root = \u0026amp;node1; // Use cur as a cursor to move around node* cur; cur = root; // Print current list printList(cur); And the printList function is as so:\nvoid printList(struct node* cur) { // Traverse through our linked list int i = 0; while (cur != NULL) { printf(\u0026#34;Item %i is %i\\n\u0026#34;, i, cur-\u0026gt;number); i++; // Move cursor to next node via pointer cur = cur-\u0026gt;next; } } Running the program gives the following:\n$ ./list Item 0 is 10 Item 1 is 20 Item 2 is 30 Item 3 is 40 Let’s change the print statement now to print out the memory locations of each of these items. Printing the value of node-\u0026gt;next should give is the address of the next node. Let’s change the printList function like so:\nvoid printList(struct node* cur) { // Traverse through our linked list while (cur != NULL) { printf(\u0026#34;%i is is at position %p\\n\u0026#34;, cur-\u0026gt;number, \u0026amp;cur-\u0026gt;number); printf(\u0026#34;Next node is at position %p\\n\\n\u0026#34;, cur-\u0026gt;next); // Move cursor to next node via pointer cur = cur-\u0026gt;next; } } Printing this out gives us the following:\n$ ./list 10 is is at position 0xbff38e68 Next node is at position 0xbff38e60 20 is is at position 0xbff38e60 Next node is at position 0xbff38e58 30 is is at position 0xbff38e58 Next node is at position 0xbff38e50 40 is is at position 0xbff38e50 Next node is at position (nil) Notice the memory address of each next value is the same as the address of the next value printed. Exactly what we would expect. Also notice that node4’s next value is NULL. I’ve used this very fact in the while loop to know when the list has ended and exit the loop.\nAdding to the list The previous program had me hard code four node values into the program. Let’s change this now so that the program will prompt to add values, and then print them out when needed:\n#include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; // Struct node with pointer to struct node typedef struct node { int number; struct node* next; }node; void printList(node* cur) { // Traverse through our linked list while (cur != NULL) { printf(\u0026#34;%i is is at position %p\\n\u0026#34;, cur-\u0026gt;number, \u0026amp;cur-\u0026gt;number); printf(\u0026#34;Next node is at position %p\\n\\n\u0026#34;, cur-\u0026gt;next); // Move cursor to next node via pointer cur = cur-\u0026gt;next; } } void addNode(node* cur) { // Traverse through our linked list // Need to get to end of list in order to append if (cur != NULL) { while (cur-\u0026gt;next != NULL) { cur = cur-\u0026gt;next; } } // Now at the the end of the list, ensure the current last node // points to a new node we create dynamically cur-\u0026gt;next = (node*) malloc(sizeof(node)); // Move to the new node cur = cur-\u0026gt;next; // Get a value inside that node printf(\u0026#34;Please enter node value: \u0026#34;); scanf(\u0026#34;%i\u0026#34;, \u0026amp;cur-\u0026gt;number); } int main(void) { // Create initial node node node1; // Get initial value from user printf(\u0026#34;Please enter inital node value: \u0026#34;); scanf(\u0026#34;%i\u0026#34;, \u0026amp;node1.number); // Initial node\u0026#39;s next should be end of list node1.next = NULL; // Root position of list node* root = \u0026amp;node1; // Use cur as a cursor to move around node* cur; // User controls what to do next int choice; do { printf(\u0026#34;1 - Add new item to list\\n\u0026#34;); printf(\u0026#34;2 - Print list\\n\u0026#34;); printf(\u0026#34;3 - Exit\\n# \u0026#34;); scanf(\u0026#34;%i\u0026#34;, \u0026amp;choice); switch (choice) { case 3: return 0; case 2: cur = root; printList(cur); break; case 1: cur = root; addNode(cur); } } while (choice != 3); } Let’s add a couple of values and then print them out:\n: ./list Please enter inital node value: 100 1 - Add new item to list 2 - Print list 3 - Exit # 1 Please enter node value: 200 1 - Add new item to list 2 - Print list 3 - Exit # 2 100 is is at position 0xbf97dc18 Next node is at position 0x875a008 200 is is at position 0x875a008 Next node is at position (nil) 1 - Add new item to list 2 - Print list 3 - Exit # 1 Please enter node value: 300 1 - Add new item to list 2 - Print list 3 - Exit # 1 Please enter node value: 400 1 - Add new item to list 2 - Print list 3 - Exit # 2 100 is is at position 0xbf97dc18 Next node is at position 0x875a008 200 is is at position 0x875a008 Next node is at position 0x875a018 300 is is at position 0x875a018 Next node is at position 0x875a028 400 is is at position 0x875a028 Next node is at position (nil) 1 - Add new item to list 2 - Print list 3 - Exit # 3 Inserting into the list Inserting into a list is very simple too, but it comes with one risky part. Order of operations is quite important. If you get to the position where you want to insert, then create a new node, you need to be sure that you don’t lose the address to the original next node. If you do so, you could lose the rest of your list!\nOnce again, imagery helps to explain this. Here we have three nodes. We now want to add a new node in the position:\nHere we malloc a new node, and then point the previous node to this new node:\nOops, how do we now reattach the rest of the list? It’s there in memory, but good luck finding it.\nIt’s essential to ensure you either link the new node to the old rest of list first, then change the old node:\nOr simply save the pointer value that the original node was pointing to, and copy that back into our new node’s pointer value.\nI’ve chose the latter in the following code. I’ll only post the changed parts. I’ll create a new function that will do the insertion and call it through main():\ndo { printf(\u0026#34;1 - Add new item to list\\n\u0026#34;); printf(\u0026#34;2 - Print list\\n\u0026#34;); printf(\u0026#34;3 - Insert node\\n\u0026#34;); printf(\u0026#34;4 - Exit\\n# \u0026#34;); scanf(\u0026#34;%i\u0026#34;, \u0026amp;choice); switch (choice) { case 4: return 0; case 3: cur = root; printf(\u0026#34;After which value should I insert? \u0026#34;); int match; scanf(\u0026#34;%i\u0026#34;, \u0026amp;match); insertNode(cur, match); break; case 2: cur = root; printList(cur); break; case 1: cur = root; addNode(cur); } } while (choice != 4); void insertNode(node* cur, int match) { // Traverse through linked list to find value while (cur-\u0026gt;next != NULL) { // We\u0026#39;ve found the node we\u0026#39;re looking for if (cur-\u0026gt;number == match) { // save pointer to rest of list node* temp = cur-\u0026gt;next; // Create a new node and get current node to point to it cur-\u0026gt;next = (node*) malloc(sizeof(node)); // Move to the new node cur = cur-\u0026gt;next; // Get a value to insert printf(\u0026#34;What value do you want to insert? \u0026#34;); int number; scanf(\u0026#34;%i\u0026#34;, \u0026amp;number); cur-\u0026gt;number = number; // Finally ensure new node connects to the rest of the list cur-\u0026gt;next = temp; return; } // Otherwise keep going through the list cur = cur-\u0026gt;next; } } Let’s add some values and then insert one:\n$ ./list Please enter inital node value: 10 1 - Add new item to list 2 - Print list 3 - Insert node 4 - Exit # 1 Please enter node value: 20 1 - Add new item to list 2 - Print list 3 - Insert node 4 - Exit # 1 Please enter node value: 30 1 - Add new item to list 2 - Print list 3 - Insert node 4 - Exit # 3 After which value should I insert? 20 What value do you want to insert? 25 1 - Add new item to list 2 - Print list 3 - Insert node 4 - Exit # 2 10 is is at position 0xbfd42938 Next node is at position 0x831e008 20 is is at position 0x831e008 Next node is at position 0x831e028 25 is is at position 0x831e028 Next node is at position 0x831e018 30 is is at position 0x831e018 Next node is at position (nil) 1 - Add new item to list 2 - Print list 3 - Insert node 4 - Exit # 4 Deleting a node Deleting is similar to inserting. Instead of getting a new value, we simply look for the node in question and unlink it from the chain. As per the inserting function, we need to take care that we don’t lose the address of the next node in the chain. It’s a little more tricky as well, as when we remove a node, we need to get back to the previous node so we can set it’s pointer to the next node we need:\nHere I’ve deleted the second node. The first node’s pointer now points to a memory location with garbage data. We’ve also lost the address to the rest of the list!\nIn my function here, I’ll ensure I always keep the pointer address of the previous node. When we find the node we want to remove, we still have the previous pointer value that we can copy where we need it:\nvoid deleteNode(node* cur, int match) { // We need to keep note of previous node // otherwise we cannot reconnect it! node* previous = cur; // Traverse through linked list to find value while (cur-\u0026gt;next != NULL) { // We\u0026#39;ve found the node we\u0026#39;re looking for if (cur-\u0026gt;number == match) { // Set pointer of last node around this node to new node node* temp = cur-\u0026gt;next; cur = previous; cur-\u0026gt;next = temp; return; // Where to release the RAM? } // Otherwise keep going through the list previous = cur; cur = cur-\u0026gt;next; } } : ./list Please enter inital node value: 10 1 - Add new item to list 2 - Print list 3 - Insert node 4 - Delete node 5 - Exit # 1 Please enter node value: 20 1 - Add new item to list 2 - Print list 3 - Insert node 4 - Delete node 5 - Exit # 3 After which value should I insert? 10 What value do you want to insert? 15 1 - Add new item to list 2 - Print list 3 - Insert node 4 - Delete node 5 - Exit # 2 10 is is at position 0xbfa0b0c8 Next node is at position 0x8bda018 15 is is at position 0x8bda018 Next node is at position 0x8bda008 20 is is at position 0x8bda008 Next node is at position (nil) 1 - Add new item to list 2 - Print list 3 - Insert node 4 - Delete node 5 - Exit # 4 Which value should I delete? 15 1 - Add new item to list 2 - Print list 3 - Insert node 4 - Delete node 5 - Exit # 2 10 is is at position 0xbfa0b0c8 Next node is at position 0x8bda008 20 is is at position 0x8bda008 Next node is at position (nil) 1 - Add new item to list 2 - Print list 3 - Insert node 4 - Delete node 5 - Exit #5 Freeing your nodes It’s good practice to clean up once you’re done. Just before we exit, I want to ensure that all nodes are removed from memory. As noted in the delete section, we need to ensure that we save the location of the next node. There are a couple of ways to do this. One way is to keep a note of the next node’s address, then delete the current node. then off to the next node and repeat. I’ve done that here:\nvoid deleteList(node* cur) { // Traverse through linked list while (cur != NULL) { node* temp = cur; cur = cur-\u0026gt;next; free (temp); } } Extras I’ve not covered all the different ways of manipulating a linked list. What if we want to delete the first or last node for example?\nThis is a good read if you want to take it further: https://www.cs.bu.edu/teaching/c/linked-list/delete/\nYou can find my working code for the above right here: https://github.com/mellowdrifter/C-Sandbox/blob/master/linked.c\n","permalink":"https://mellowd.co.uk/post/linked-lists/","summary":"\u003cp\u003ePython has a rather handy list method. It allows you to add and remove items at will. \u003ca href=\"http://www.laurentluce.com/posts/python-list-implementation/\"\u003eHow it actually does this is rather elaborate and you can read all about it over here.\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eC doesn’t give you the same flexibility. When you create an array, it is of X size. That size cannot change as there is no guarantee that memory on either side of the existing array space is even free for you. For this reason there is a handy data structure called the linked list.\u003c/p\u003e","title":"Linked lists"},{"content":"Back in March 2014 I wrote an app in Python that would log in and check various OSPF properties.\nWhen putting the data into a structure, I was limited both by knowledge and Python at the time. I ended up using a dictionary which worked rather well, but I was never 100% happy with it.\nRecently I’ve stumbled across structs in C, in which I can make pretty much any data structure I would like.\nThe Python app above pulled some very specific results. Looks at each OSPF-enabled interface on a router and get it’s properties. Most of those properties are integers, but the interface name itself is a string. Each interface has n amount of properties, and each router itself has n amount of OSPF interfaces.\nAn interface therefore looks like so:\nInterface name - String IP address - String Cost - Int Adj - Int Hello timer - Int Dead timer - Int And above that, a router looks like so:\nRouter name - String Interface - Struct Interface - Struct A struct can itself contain structs. I’ll explain more on this later.\nStruct A struct is a data structure. Some amount of RAM is given back by the OS to contain the data you require. If we look back at the interface above, it has a certain amount of fields, and each of those fields contain different data types. Let’s create one in C:\nstruct ospfInt { char intName[15]; char ipAddress[15]; int cost; int adj; int hello; int dead; }; Here we have created a data structure, that contains 2 strings with 16 characters, and 4 integers. Let’s use this in a program and manually insert properties into the struct for now. I’ll manually define two interfaces and then pass those structs to a function to print out the various properties:\n#include \u0026lt;stdio.h\u0026gt; #include \u0026lt;string.h\u0026gt; struct ospfInt { char intName[15]; char ipAddress[15]; int cost; int adj; int hello; int dead; }; void printInterface(struct ospfInt interface) { // Print out structs passed here printf(\u0026#34;Interface name: %s\\n\u0026#34;, interface.intName); printf(\u0026#34;IP Address: %s\\n\u0026#34;, interface.ipAddress); printf(\u0026#34;Cost: %i\\n\u0026#34;, interface.cost); printf(\u0026#34;Adj: %i\\n\u0026#34;, interface.adj); printf(\u0026#34;Hello: %i\\n\u0026#34;, interface.hello); printf(\u0026#34;Dead: %i\\n\\n\u0026#34;, interface.dead); } int main(void) { // Create two interfaces with type struct ospfInt struct ospfInt interface1; struct ospfInt interface2; //Insert properties into those structs strcpy(interface1.intName, \u0026#34;ge-1/3/0.641\u0026#34;); strcpy(interface1.ipAddress, \u0026#34;10.11.31.227\u0026#34;); interface1.cost = 10; interface1.adj = 1; interface1.hello = 10; interface1.dead = 40; strcpy(interface2.intName, \u0026#34;lo0.0\u0026#34;); strcpy(interface2.ipAddress, \u0026#34;10.11.225.224\u0026#34;); interface2.cost = 0; interface2.adj = 0; interface2.hello = 3; interface2.dead = 12; // Print out properties via function printInterface(interface1); printInterface(interface2); } The end result of the above being like so:\n$ ./structs Interface name: ge-1/3/0.641 IP Address: 10.11.31.227 Cost: 10 Adj: 1 Hello: 10 Dead: 40 Interface name: lo0.0 IP Address: 10.11.225.224 Cost: 0 Adj: 0 Hello: 3 Dead: 12 typedef struct Instead of initialising interface1 and interface2 above as type struct, I can just typedef the struct itself. So change this:\nstruct ospfInt { char intName[15]; char ipAddress[15]; int cost; int adj; int hello; int dead; }; to this:\ntypedef struct { char intName[15]; char ipAddress[15]; int cost; int adj; int hello; int dead; }ospfInt; Now in order to create a structure of type ospfInt above, simply initialise it like so:\n// Create two interfaces with type struct ospfInt ospfInt interface1; ospfInt interface2; Remembering to change all references to it to simply state ospfInt:\nvoid printInterface(ospfInt interface) { // Print out structs passed here } attribute packed C compilers will align data inside the structure to fit inside 4 byte structures. This has got to do with the way that data is read by CPUs these days. What this means though is that the amount of memory a structure of yours needs might not be the sum total of the data types inside. I’ll create a new structure called interface1 and check each of it’s components size. I’ll then print out the actual size on the system.\ntypedef struct { char intName[15]; char ipAddress[15]; int cost; int adj; int hello; int dead; }ospfInt; int main(void) { // Create an inteface with type struct ospfInt ospfInt interface1; // Print out size of each datatype inside struct printf(\u0026#34;Bytes used in intName[15] = %lu\\n\u0026#34;, sizeof(interface1.intName)); printf(\u0026#34;Bytes used in ipAddress[15] = %lu\\n\u0026#34;, sizeof(interface1.ipAddress)); printf(\u0026#34;Bytes used in cost = %lu\\n\u0026#34;, sizeof(interface1.cost)); printf(\u0026#34;Bytes used in adj = %lu\\n\u0026#34;, sizeof(interface1.adj)); printf(\u0026#34;Bytes used in hello = %lu\\n\u0026#34;, sizeof(interface1.hello)); printf(\u0026#34;Bytes used in dead = %lu\\n\u0026#34;, sizeof(interface1.dead)); printf(\u0026#34;Bytes total = %lu\u0026#34;, sizeof(interface1.intName) + sizeof(interface1.ipAddress) + sizeof(interface1.cost) + sizeof(interface1.adj) + sizeof(interface1.hello) + sizeof(interface1.dead)); printf(\u0026#34;\\n\\nActual byes used = %lu\\n\u0026#34;, sizeof(interface1)); This code gives this output on my Mac:\n$ ./structs Bytes used in intName[15] = 15 Bytes used in ipAddress[15] = 15 Bytes used in cost = 4 Bytes used in adj = 4 Bytes used in hello = 4 Bytes used in dead = 4 Bytes total = 46 Actual byes used = 48 The reason here is because the two strings are taking up 15 bytes each. As this is not on a four byte boundary, each one has been padded with an extra byte to take it to a multiple of four. If I were to change the struct to 16, these values should match up:\ntypedef struct { char intName[16]; char ipAddress[16]; int cost; int adj; int hello; int dead; }ospfInt; $ ./structs Bytes used in intName[16] = 16 Bytes used in ipAddress[16] = 16 Bytes used in cost = 4 Bytes used in adj = 4 Bytes used in hello = 4 Bytes used in dead = 4 Bytes total = 48 Actual byes used = 48 You can inform the compiler not to align data this way. If you’re reading structures from somewhere else, it’s important that data values fall on boundaries that you would expect. To remove alignment you create the structure like so:\ntypedef struct { char intName[15]; char ipAddress[15]; int cost; int adj; int hello; int dead; }__attribute__((__packed__)) ospfInt; As alignment is removed, no padding will be done and the struct should be exactly 46 bytes:\n$ ./structs Bytes used in intName[15] = 15 Bytes used in ipAddress[15] = 15 Bytes used in cost = 4 Bytes used in adj = 4 Bytes used in hello = 4 Bytes used in dead = 4 Bytes total = 46 Actual byes used = 46 Struct of structs The data types inside structs can be any data type, including other structs. I now want to create the router struct, which contains it’s name, it’s loopback IP address, and all the OSPF enabled interface structures. There are a couple of ways I could do this and I’ll go over both. Let’s first assume that a router will not have more than 500 OSPF enabled interfaces to begin with. I could create a struct of structs like so:\ntypedef struct { char routerName[20]; char loopAddress[15]; ospfInt interfaces[500]; }ospfRouter; Create a function that will then print out the router details, plus its two interfaces like so:\nvoid printRouter(ospfRouter router) { // Print out router information and pass // interfaces for printing printf(\u0026#34;Router name: %s\\n\u0026#34;, router.routerName); printf(\u0026#34;Loopback address: %s\\n\\n\u0026#34;, router.loopAddress); for (int i = 0; i \u0026lt; 2; i++) { printInterface(router.interfaces[i]); } } int main(void) { printRouter(router1); } The result being:\n$ ./structs Router name: r1.com Loopback address: 10.10.10.10 Interface name: ge-1/3/0.641 IP Address: 10.11.31.227 Cost: 10 Adj: 1 Hello: 10 Dead: 40 Interface name: lo0.0 IP Address: 10.11.225.224 Cost: 0 Adj: 0 Hello: 3 Dead: 12 Another way would be to define pointers in the router struct. This way you could create a load of interface structs, and have the router struct itself simply point to those variables. I can’t think of any benefit to this though. You would have to keep a load of interface variables and to me it seems it would be better to stick that inside the router struct itself.\n","permalink":"https://mellowd.co.uk/post/structs-in-c/","summary":"\u003cp\u003eBack in March 2014 \u003ca href=\"https://github.com/mellowdrifter/Cisco-OSPF-Checker\"\u003eI wrote an app in Python that would log in and check various OSPF properties.\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eWhen putting the data into a structure, I was limited both by knowledge and Python at the time. I ended up using a \u003ca href=\"https://docs.python.org/2/tutorial/datastructures.html#dictionaries\"\u003edictionary\u003c/a\u003e which worked rather well, but I was never 100% happy with it.\u003c/p\u003e\n\u003cp\u003eRecently I’ve stumbled across structs in C, in which I can make pretty much any data structure I would like.\u003c/p\u003e","title":"Structs in C"},{"content":"I’ve been doing a lot of coding practise, and recursive functions are one of those things that’s easier to visualise than think about.\nAt it’s heart, a recursive function is one that calls itself in over and over again until a result is found. At first that doesn’t make sense so let’s look at an example.\nFactorial The factorial of a number is the product off all numbers from that number down to 1. Two examples follow:\n3! = 3 X 2 X 1 = 6 4! = 4 X 3 X 2 X 1 = 24 etc... Looking closely, the factorial of any number is the product of that number and the factorial of the number below. i.e. The factorial of 4 is the same as 4 X the factorial of 3. Carrying on with this example, the factorial of 3 is the same as 3 X the factorial of 2.\nThis is a perfect use case of a recursive function, as a function can work out the factorial of any number by multiplying it by the factorial of a number 1 smaller. It is important to ensure that this recursiveness does not happen indefinitely. If a function calls itself forever, you’ll have a loop that never ends. In the above factorial, as soon as we get to 1, there is no need to call the function again, rather that should be used as an exit to the function.\nI’ll write up a quick factorial recursive function in C:\n#include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; int factorial(int number) { //provide a way out of the function if (number == 1) { return 1; } // otherwide multiply value with factorial of smaller number else { return number * factorial(number - 1); } } int main(int argc, char* argv[]) { int number = atoi(argv[1]); printf(\u0026#34;The factorial of %i is %i\\n\u0026#34;, number, factorial(number)); } So the program is simple enough. Take a number via an argument, convert it to an integer, then print out the factorial of it. Let’s pass a few values to see the result:\n$ ./factorial 3 The factorial of 3 is 6 $ ./factorial 4 The factorial of 4 is 24 $ ./factorial 5 The factorial of 5 is 120 $ ./factorial 6 The factorial of 6 is 720 If I pass a factorial of 4, what I’m actually doing is as follows:\n4 X (3 X (2 X (1))) Each call to factorial will call the function again with a number 1 smaller. This continues until the number is one. This is then returned to the previous function, returned to the previous function, and so on. i.e. The function will be continue to call itself until it gets to 1. That value will then be returned to the calling function and so on. In essence it gets to 1, then works it way out from the inside.\nIf I left out the case where number == 1, this function would loop forever. Let’s give this a test:\nint factorial(int number) { return number * factorial(number - 1); } $ ./factorial 3 Bus error (core dumped) Nope. I do wonder how far it got before it gets to this message though.\nint factorial(int number) { printf(\u0026#34;number is now %i\\n\u0026#34;, number); return number * factorial(number - 1); } $ ./factorial 3 \u0026gt; error.txt Bus error (core dumped) $ tail error.txt number is now -261597 number is now -261598 number is now -261599 number is now -261600 number is now -261601 number is now -261602 number is now -261603 number is now -261604 number is now -261605 number is now -261606 So we got to -261606 before this program crashed. But why did it crash? Should this function not just continue on forever, subtracting 1 each time? The reason has to do with how recursive functions are viewed in memory. Looking back at the factorial above, I noted that this is how the function saw the factorial of 4:\n4 X (3 X (2 X (1))) The computer needs to work this out from the inside out. i.e. It needs to get to number 1, to return that value to the previous call, which then passes that number back to the previous call to the function. Each time a function calls itself, that function gets added to the stack.\nLet’s visualise this. The program has just started and we have a stack up on which main has been called:\nmain() calls factorial(), which gets added to the stack:\nThere is a finite limit to how big the stack is, and so a function can only call itself so many times. The larger the original number in our factorial calculation, the larger the amount of times our function calls itself. At some point we are going to run out of space on our stack. Remember one we get to the end, the result of the final calculation will be passed to the previous function, where it’s result will then be passed back and so on. Storing those functions and variables take space.\nAt which point the program will crash. I’ll now change the program again to show the memory location of the variable passed into it each time. A new memory location will be used each time, until we run out:\nint factorial(int number) { int* pointer = \u0026amp;number; printf(\u0026#34;number is now %i and memory location is %p\\n\u0026#34;, number, pointer); return number * factorial(number - 1); } $ ./factorial 3 \u0026gt; error.txt Bus error (core dumped) $ tail error.txt number is now -174516 and memory location is 0xbf68ea14 number is now -174517 and memory location is 0xbf68e9e4 number is now -174518 and memory location is 0xbf68e9b4 number is now -174519 and memory location is 0xbf68e984 number is now -174520 and memory location is 0xbf68e954 number is now -174521 and memory location is 0xbf68e924 number is now -174522 and memory location is 0xbf68e8f4 number is now -174523 and memory location is 0xbf68e8c4 number is now -174524 and memory location is 0xbf68e894 Overflowing the stack is called a stack overflow. So at least you now know where the name stackoverflow.com comes from.\nThere is a program called valgrind that will actually show us what error was encountered:\n$ valgrind ./factorial 3 number is now -174555 and memory location is 0xbe233654 ==3145== Stack overflow in thread 1: can\u0026#39;t grow stack to 0xbe232ffc ==3145== ==3145== Process terminating with default action of signal 11 (SIGSEGV): dumping core ==3145== Access not within mapped region at address 0xBE232FFC ==3145== at 0x40FAB9C: _IO_file_write@@GLIBC_2.1 (fileops.c:1261) ==3145== If you believe this happened as a result of a stack ==3145== overflow in your program\u0026#39;s main thread (unlikely but ==3145== possible), you can try to increase the size of the ==3145== main thread stack using the --main-stacksize= flag. ==3145== The main thread stack size used in this run was 8388608. ==3145== Stack overflow in thread 1: can\u0026#39;t grow stack to 0xbe232ff8 ==3145== ==3145== Process terminating with default action of signal 11 (SIGSEGV) ==3145== Access not within mapped region at address 0xBE232FF8 ==3145== at 0x4024510: _vgnU_freeres (in /usr/lib/valgrind/vgpreload_core-x86-linux.so) ==3145== If you believe this happened as a result of a stack ==3145== overflow in your program\u0026#39;s main thread (unlikely but ==3145== possible), you can try to increase the size of the ==3145== main thread stack using the --main-stacksize= flag. ==3145== The main thread stack size used in this run was 8388608. ==3145== ==3145== HEAP SUMMARY: ==3145== in use at exit: 0 bytes in 0 blocks ==3145== total heap usage: 0 allocs, 0 frees, 0 bytes allocated ==3145== ==3145== All heap blocks were freed -- no leaks are possible ==3145== ==3145== For counts of detected and suppressed errors, rerun with: -v ==3145== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) Segmentation fault (core dumped) So there you have it :)\n","permalink":"https://mellowd.co.uk/post/visualising-recursive-functions/","summary":"\u003cp\u003eI’ve been doing a lot of coding practise, and recursive functions are one of those things that’s easier to visualise than think about.\u003c/p\u003e\n\u003cp\u003eAt it’s heart, a recursive function is one that calls itself in over and over again until a result is found. At first that doesn’t make sense so let’s look at an example.\u003c/p\u003e\n\u003ch2 id=\"factorial\"\u003eFactorial\u003c/h2\u003e\n\u003cp\u003eThe factorial of a number is the product off all numbers from that number down to 1. Two examples follow:\u003c/p\u003e","title":"Visualising recursive functions"},{"content":"I’ve had zero time to update the blog recently. As some of you may know, I recently started a new job with Google. I’ve moved my family and I over from the UK to Dublin, Ireland.\nTo say I’m busy right now is an understatement. Not only is there a ton of reading for me to do at work, I’m also trying to sort out all the issues of moving country again.\nThere will be updates coming eventually, but when that will be I’m not yet certain. I’m working regular posts as well as a new part of the site that’ll eventually come live. I’m also working on a few twitter based apps. Two in the wild already are my @bgp4_table and @bgp6_table accounts.\nIn the meantime feel free to continue commenting. I do read all and try and reply when it’s possible.\n","permalink":"https://mellowd.co.uk/post/life-is-busy/","summary":"\u003cp\u003eI’ve had zero time to update the blog recently. As some of you may know, I recently started a new job with \u003ca href=\"https://www.google.com/\"\u003eGoogle\u003c/a\u003e. I’ve moved my family and I over from the UK to \u003ca href=\"http://en.wikipedia.org/wiki/Dublin\"\u003eDublin, Ireland\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eTo say I’m busy right now is an understatement. Not only is there a ton of reading for me to do at work, I’m also trying to sort out all the issues of moving country again.\u003c/p\u003e","title":"Life is busy"},{"content":"Let me preface this post by stating I am not a database expert. I use them occasionally now and then. The below post probably doesn’t show best practices. If you have any suggestions feel free to comment.\nOver the weekend I’ve been testing various ways for me to store, update, and retrieve data from a database. At first I was using the sqlite3 package, but turned to MySQL. Why the move you ask? Well I already had MySQL running on the target machine and already had scripts backing up all databases every night. Why not just use the existing database there?\nFor this post I’m using python 2.7.3, Debian 7.7.0, and MySQL-python 1.2.5.\nInstall and set-up Ensure that the mysql python library is installed:\nroot@python-db:/tmp# pip install MySQL-python Collecting MySQL-python Downloading MySQL-python-1.2.5.zip (108kB) 100% |################################| 110kB 6.4MB/s Installing collected packages: MySQL-python I’ve also installed mysql on the test server. I’m using a root password of password simply as this is a test box.\nI’ll now create a database to work on. I want a database that contains BGP AS numbers, AS Names, a queried field, and a data to know when last the AS changed. A user will be created that has full access to this database. Password for now will be password.\nroot@python-db:~# mysql -u root -p Enter password: mysql\u0026gt; create database AS_NUM_NAME; Query OK, 1 row affected (0.00 sec) mysql\u0026gt; CREATE USER \u0026#39;asnuser\u0026#39;@\u0026#39;localhost\u0026#39; IDENTIFIED BY \u0026#39;password\u0026#39;; Query OK, 0 rows affected (0.00 sec) mysql\u0026gt; GRANT ALL ON AS_NUM_NAME.* TO \u0026#39;asnuser\u0026#39;@\u0026#39;localhost\u0026#39;; Query OK, 0 rows affected (0.00 sec) mysql\u0026gt; USE AS_NUM_NAME; Database changed mysql\u0026gt; create table ASN(AS_NUM INT, AS_NAME VARCHAR(50), QUERIED INT, CHANGED DATE); Query OK, 0 rows affected (0.01 sec) mysql\u0026gt; flush privileges; Query OK, 0 rows affected (0.00 sec) mysql\u0026gt; quit; Bye Add data The mysql library allows us to open a connection to the database and execute sql commands. The next script will open the database, insert new data, commit those changed, then close the database:\n#!/usr/bin/python import MySQLdb import time today = time.strftime(\u0026#34;%Y-%m-%d\u0026#34;) db = MySQLdb.connect(\u0026#34;localhost\u0026#34;, \u0026#34;asnuser\u0026#34;, \u0026#34;password\u0026#34;, \u0026#34;AS_NUM_NAME\u0026#34;) with db: cursor = db.cursor() sql = \u0026#39;\u0026#39;\u0026#39;INSERT INTO ASN(AS_NUM, AS_NAME, QUERIED, CHANGED) \\ VALUES (%s, %s, %s, %s)\u0026#39;\u0026#39;\u0026#39; cursor.execute(sql, (1, \u0026#39;Level 3 Communications, Inc.\u0026#39;, 0, today)) I’ll give this a run:\nroot@python-db:~# ./add.py Log into the database to see the changes:\nroot@python-db:~# mysql -u asnuser -p Enter password: mysql\u0026gt; use AS_NUM_NAME; mysql\u0026gt; select * from ASN; +--------+------------------------------+---------+------------+ | AS_NUM | AS_NAME | QUERIED | CHANGED | +--------+------------------------------+---------+------------+ | 1 | Level 3 Communications, Inc. | 0 | 2015-01-06 | +--------+------------------------------+---------+------------+ 1 row in set (0.00 sec) Of course, doing an single update is not very useful. I’ll create a text file with the first 20 AS numbers and names in use:\n1 Level 3 Communications, Inc. 2 University of Delaware 3 Massachusetts Institute of Technology 4 University of Southern California 5 Symbolics, Inc. 6 Bull HN Information Systems Inc. 7 UK Defence Research Agency 8 Rice University 9 Carnegie Mellon University 10 CSNET Coordination and Information Center (CSNET-CIC) 11 Harvard University 12 New York University 13 Headquarters, USAISC 14 Columbia University 15 DYNAMICS 16 Lawrence Berkeley National Laboratory 17 Purdue University 18 University of Texas at Austin 19 Leidos, Inc. 20 University of Rochester I want to extract the first number, then everything will be part of the AS name. I’ll then stick them all in the database:\n#!/usr/bin/python import MySQLdb import time today = time.strftime(\u0026#34;%Y-%m-%d\u0026#34;) as_list = [] with open (\u0026#39;20_asn\u0026#39;) as f: new_as = f.readlines() for AS in new_as: as_list.append(AS.strip()) db = MySQLdb.connect(\u0026#34;localhost\u0026#34;, \u0026#34;asnuser\u0026#34;, \u0026#34;password\u0026#34;, \u0026#34;AS_NUM_NAME\u0026#34;) with db: cursor = db.cursor() sql = \u0026#39;\u0026#39;\u0026#39;INSERT INTO ASN(AS_NUM, AS_NAME, QUERIED, CHANGED) \\ VALUES (%s, %s, %s, %s)\u0026#39;\u0026#39;\u0026#39; for AS in as_list: split_as = AS.split(\u0026#39; \u0026#39;, 1) cursor.execute(sql, (int(split_as[0].strip()), split_as[1].strip(), 0, today)) After a quick run, let’s log back into the database and check what we have\nmysql\u0026gt; SELECT * FROM ASN WHERE AS_NUM = 14; +--------+---------------------+---------+------------+ | AS_NUM | AS_NAME | QUERIED | CHANGED | +--------+---------------------+---------+------------+ | 14 | Columbia University | 0 | 2015-01-06 | +--------+---------------------+---------+------------+ 1 row in set (0.00 sec) mysql\u0026gt; SELECT * FROM ASN WHERE AS_NUM = 18; +--------+-------------------------------+---------+------------+ | AS_NUM | AS_NAME | QUERIED | CHANGED | +--------+-------------------------------+---------+------------+ | 18 | University of Texas at Austin | 0 | 2015-01-06 | +--------+-------------------------------+---------+------------+ 1 row in set (0.00 sec) Retrieve data I’ll write another script now that takes an AS number as an argument and prints out the AS name:\n#!/usr/bin/python import MySQLdb import sys db = MySQLdb.connect(\u0026#34;localhost\u0026#34;, \u0026#34;asnuser\u0026#34;, \u0026#34;password\u0026#34;, \u0026#34;AS_NUM_NAME\u0026#34;) cursor = db.cursor() sql = \u0026#34;SELECT AS_NAME FROM ASN where AS_NUM = \u0026#34; + sys.argv[1] with db: cursor.execute(sql) row = cursor.fetchone() print row[0] Note: Replies are always given as tuples, even when a single value is returned. This is why I’m only printing the first item in the returned tuple with [0]\nA quick run gives me what I need:\nroot@python-db:~# ./check.py 5 Symbolics, Inc. root@python-db:~# ./check.py 6 Bull HN Information Systems Inc. root@python-db:~# ./check.py 7 UK Defence Research Agency Updating I’d like to update the QUERIED field each time I query a value. I’ll rewrite the last script to update that field when it gets a result. First it should get the AS Name and the QUERIED value. Then update the QUERIED value by 1, and print the AS name:\n#!/usr/bin/python import MySQLdb import sys query_as = sys.argv[1] db = MySQLdb.connect(\u0026#34;localhost\u0026#34;, \u0026#34;asnuser\u0026#34;, \u0026#34;password\u0026#34;, \u0026#34;AS_NUM_NAME\u0026#34;) cursor = db.cursor() sql = \u0026#34;SELECT * FROM ASN where AS_NUM = \u0026#34; + sys.argv[1] with db: cursor.execute(sql) row = cursor.fetchone() queried = row[2] queried += 1 sql = \u0026#34;\u0026#34;\u0026#34; UPDATE ASN SET QUERIED = %s WHERE AS_NUM = %s\u0026#34;\u0026#34;\u0026#34; cursor.execute(sql, (queried, query_as)) print row[1] First, my mysql check the queried value is 0:\nmysql\u0026gt; select * from ASN where AS_NUM = 18; +--------+-------------------------------+---------+------------+ | AS_NUM | AS_NAME | QUERIED | CHANGED | +--------+-------------------------------+---------+------------+ | 18 | University of Texas at Austin | 0 | 2015-01-06 | +--------+-------------------------------+---------+------------+ 1 row in set (0.00 sec) Query that value three times:\nroot@python-db:~# ./check.py 18 University of Texas at Austin root@python-db:~# ./check.py 18 University of Texas at Austin root@python-db:~# ./check.py 18 University of Texas at Austin Now check the database:\nmysql\u0026gt; select * from ASN where AS_NUM = 18; +--------+-------------------------------+---------+------------+ | AS_NUM | AS_NAME | QUERIED | CHANGED | +--------+-------------------------------+---------+------------+ | 18 | University of Texas at Austin | 3 | 2015-01-06 | +--------+-------------------------------+---------+------------+ 1 row in set (0.00 sec) Very useful.\nThere was an issue I created earlier that you may have spotted. I created a record for ASN1, then when I created the first 20 I created another ASN1. This can be shown via a query:\nmysql\u0026gt; SELECT * FROM ASN WHERE AS_NUM = 1; +--------+------------------------------+---------+------------+ | AS_NUM | AS_NAME | QUERIED | CHANGED | +--------+------------------------------+---------+------------+ | 1 | Level 3 Communications, Inc. | 0 | 2015-01-06 | | 1 | Level 3 Communications, Inc. | 0 | 2015-01-06 | +--------+------------------------------+---------+------------+ 2 rows in set (0.00 sec) The script I used to populate all 20 was fine to populate a database for the first time, but no good for further updates. The script simply created new records, with new dates. What we want is to check the database first, then do different actions depending on what we see.\nI’ll now get a list of the first 30 AS numbers, then run a script to update. I’ll need to check the following:\nDoes the AS Number already exist? If so, check that the name matches (note, I’m storing only 50 characters of the name, so only match the first 50 characters) If the name doesn’t match, update the name and updated the CHANGED field. Otherwise just create a new record and insert todays date into the CHANGED field. I’ll first delete all records with ASN 1 from the database then write the new script.\nmysql\u0026gt; DELETE FROM ASN WHERE AS_NUM = 1; Query OK, 2 rows affected (0.00 sec) As this was getting a bit big, I moved the update and create sections into their own methods.\n#!/usr/bin/python import MySQLdb import sys import time as_list = [] already, new, changed = 0, 0, 0 today = time.strftime(\u0026#34;%Y-%m-%d\u0026#34;) with open (\u0026#39;30_asn\u0026#39;) as f: new_as = f.readlines() for AS in new_as: as_list.append(AS.strip()) db = MySQLdb.connect(\u0026#34;localhost\u0026#34;, \u0026#34;asnuser\u0026#34;, \u0026#34;password\u0026#34;, \u0026#34;AS_NUM_NAME\u0026#34;) cursor = db.cursor() def create_sql(AS_NUM, AS_NAME): sql = \u0026#39;\u0026#39;\u0026#39;INSERT INTO ASN(AS_NUM, AS_NAME, QUERIED, CHANGED) \\ VALUES (%s, %s, %s, %s)\u0026#39;\u0026#39;\u0026#39; cursor.execute(sql, (AS_NUM, AS_NAME, 0, today)) def update_sql(AS_NUM, AS_NAME): sql = \u0026#34;\u0026#34;\u0026#34; UPDATE ASN SET QUERIED = %s WHERE AS_NUM = %s\u0026#34;\u0026#34;\u0026#34; cursor.execute(sql, (AS_NUM, AS_NAME)) with db: for AS in as_list: split_as = AS.split(\u0026#39; \u0026#39;, 1) split_as[1] = split_as[1].lstrip() sql = \u0026#34;SELECT * FROM ASN where AS_NUM = \u0026#34; +str(split_as[0]) cursor.execute(sql) row = cursor.fetchone() if row: if row[1] == split_as[1][:50].strip(): already += 1 pass else: changed += 1 update_sql(int(split_as[0].strip()), split_as[1].strip()) else: new += 1 create_sql(int(split_as[0].strip()), split_as[1].strip()) print \u0026#34;New AS: \u0026#34; + str(new) print \u0026#34;Changed: \u0026#34; + str(changed) print \u0026#34;Unchanged: \u0026#34; + str(already) Let’s do a quick run:\nroot@python-db:~# ./insert.py New AS: 11 Changed: 0 Unchanged: 20 Great, 1 was re-added, plus the 10 new ones. This should add them all, plus not change the values of anything that was already there:\nmysql\u0026gt; SELECT * FROM ASN; +--------+---------------------------------------------------+---------+------------+ | AS_NUM | AS_NAME | QUERIED | CHANGED | +--------+---------------------------------------------------+---------+------------+ | 1 | Level 3 Communications, Inc. | 0 | 2015-01-06 | | 2 | University of Delaware | 2 | 2015-01-06 | | 3 | Massachusetts Institute of Technology | 1 | 2015-01-06 | | 4 | University of Southern California | 0 | 2015-01-06 | | 5 | Symbolics, Inc. | 0 | 2015-01-06 | | 6 | Bull HN Information Systems Inc. | 0 | 2015-01-06 | | 7 | UK Defence Research Agency | 0 | 2015-01-06 | | 8 | Rice University | 0 | 2015-01-06 | | 9 | Carnegie Mellon University | 0 | 2015-01-06 | | 10 | CSNET Coordination and Information Center (CSNET- | 1 | 2015-01-06 | | 11 | Harvard University | 0 | 2015-01-06 | | 12 | New York University | 0 | 2015-01-06 | | 13 | Headquarters, USAISC | 0 | 2015-01-06 | | 14 | Columbia University | 0 | 2015-01-06 | | 15 | DYNAMICS | 0 | 2015-01-06 | | 16 | Lawrence Berkeley National Laboratory | 0 | 2015-01-06 | | 17 | Purdue University | 0 | 2015-01-06 | | 18 | University of Texas at Austin | 3 | 2015-01-06 | | 19 | Leidos, Inc. | 0 | 2015-01-06 | | 20 | University of Rochester | 2 | 2015-01-06 | | 21 | The RAND Corporation | 0 | 2015-01-06 | | 22 | Navy Network Information Center (NNIC) | 0 | 2015-01-06 | | 23 | National Aeronautics and Space Administration | 0 | 2015-01-06 | | 24 | National Aeronautics and Space Administration | 0 | 2015-01-06 | | 25 | University of California at Berkeley | 0 | 2015-01-06 | | 26 | Cornell University | 0 | 2015-01-06 | | 27 | University of Maryland | 0 | 2015-01-06 | | 28 | Deutsches Zentrum fuer Luft- und Raumfahrt | 0 | 2015-01-06 | | 29 | Yale University | 0 | 2015-01-06 | | 30 | SRI International | 0 | 2015-01-06 | +--------+---------------------------------------------------+---------+------------+ 30 rows in set (0.00 sec) ","permalink":"https://mellowd.co.uk/post/python-and-mysql/","summary":"\u003cp\u003eLet me preface this post by stating I am not a database expert. I use them occasionally now and then. The below post probably doesn’t show best practices. If you have any suggestions feel free to comment.\u003c/p\u003e\n\u003cp\u003eOver the weekend I’ve been testing various ways for me to store, update, and retrieve data from a database. At first I was using the sqlite3 package, but turned to MySQL. Why the move you ask? Well I already had MySQL running on the target machine and already had scripts backing up all databases every night. Why not just use the existing database there?\u003c/p\u003e","title":"Python and MySQL"},{"content":"I created two new twitter accounts yesterday and the amount of followers in such a short time is great to see. Feel free to follow them here – @bgp4_table and @bgp6_table\nThe accounts get updated through Python, and that Python script is run via a cron job once every six hours.\nI noticed that when I ran my script manually, it worked fine. When the cronjob ran it, nothing happened. As there is no console log it made me wonder what the issue was.\nCron log The first thing I needed to do was configure cron to log it’s output. I’ve done it like so in my crontab:\n0 0,6,12,18 * * * /home/scripts/tweet.py \u0026gt; /home/scripts/tweet.log 2\u0026gt;\u0026amp;1 This directs all 1 and 2 output to the log file I created. In case you not aware, standard stream 0 is input, stream 1 is output, and stream 2 is errors. The commands above ensure I’m logging both output, if any, and errors, if any.\nPython paths On the next cron run, my log was created and it was plain to see:\n$ less tweet.log Traceback (most recent call last): File \u0026#34;/home/scripts/tweet.py\u0026#34;, line 10, in with open(\u0026#34;v4_count\u0026#34;) as f: IOError: [Errno 2] No such file or directory: \u0026#39;v4_count\u0026#39; As part of my script, I save the last values in text files located in the same path as the script. When the script runs again, it reads the last value, get the new value, then works out a delta to display. I then write the latest value into that file for the next run. This is an example of one of those reads:\n# Pull last delta with open(\u0026#34;v4_count\u0026#34;) as f: old_v4 = int(f.readline()) This ran fine if I ran the script manually, but I was always running the script from within the same folder. This meant that python was finding and opening the file in the same folder. With the cronjob, it was getting called from somwehere else where v4_count did not exist.\nThe simple fix for this was to change all references to the full path:\n# Pull last delta with open(\u0026#34;/home/scripts/v4_count\u0026#34;) as f: old_v4 = int(f.readline()) This time on the next run, no more problems :)\n","permalink":"https://mellowd.co.uk/post/python-paths-and-cron-logging/","summary":"\u003cp\u003eI created two new twitter accounts yesterday and the amount of followers in such a short time is great to see. Feel free to follow them here – \u003ca href=\"https://twitter.com/bgp4_table\"\u003e@bgp4_table\u003c/a\u003e and \u003ca href=\"https://twitter.com/bgp6_table\"\u003e@bgp6_table\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eThe accounts get updated through Python, and that Python script is run via a cron job once every six hours.\u003c/p\u003e\n\u003cp\u003eI noticed that when I ran my script manually, it worked fine. When the cronjob ran it, nothing happened. As there is no console log it made me wonder what the issue was.\u003c/p\u003e","title":"Python paths and Cron logging"},{"content":"For an electronics project I’m working on I wanted a way to check the current global routing table every five minutes for both IPv4 and IPv6. I did not want to log into anyone else’s router or looking glass as checking every 5 minutes may be considered abuse.\nSo I thought to spin up a bird routing instance. I just wanted to receive the routes, not actually install them into the kernel on my linux box. From there I’d be able to check the table size sent over.\nNat Morris helped me out by sending a full tables over a multihop BGP session.\nInstalling bird is trivial. In order to ensure I’m only running BGP and not installing into the kernel, my configuration looks pretty simple.\n/etc/bird.conf:\nlog syslog all; router id x.x.x.x; protocol device { } protocol bgp { local as xxxxx; neighbor x.x.x.x as xxxxx; multihop; password \u0026#34;xxxxx\u0026#34;; } /etc/bird6.conf:\nlog syslog all; router id x.x.x.x; protocol device { } protocol bgp { local as xxxxx; neighbor x:x:x:x:x::x as xxxxx; source address x:x:x:x:x:x::x; multihop; password \u0026#34;xxxxx\u0026#34;; } In order to get the figures I need, I’d usually have to log into the console of the daemon like so:\n$ birdc BIRD 1.3.7 ready. bird\u0026gt; show protocols all bgp1 name proto table state since info bgp1 BGP master up 19:06 Established Preference: 100 Input filter: ACCEPT Output filter: REJECT Routes: 511014 imported, 0 exported, 511014 preferred Route change stats: received rejected filtered ignored accepted Import updates: 523721 0 0 826 522895 Import withdraws: 1313 0 --- 0 1313 Export updates: 522895 522895 0 --- 0 Export withdraws: 1313 --- --- --- 0 BGP state: Established Neighbor address: x.x.x.x Neighbor AS: xxxxx Neighbor ID: x.x.x.x Neighbor caps: refresh AS4 Session: external multihop AS4 Source address: x.x.x.x Hold timer: 142/180 Keepalive timer: 33/60 I could get a script to log in and get the required information via regular expressions, but there has to be an easier way. Turns out you can push a command directly to bird without logging into it first:\n$birdc \u0026#39;show protocols all bgp1\u0026#39; BIRD 1.3.7 ready. name proto table state since info bgp1 BGP master up 19:06 Established Preference: 100 Input filter: ACCEPT Output filter: REJECT Routes: 511025 imported, 0 exported, 511025 preferred Route change stats: received rejected filtered ignored accepted Import updates: 523924 0 0 839 523085 Import withdraws: 1329 0 --- 0 1329 Export updates: 523085 523085 0 --- 0 Export withdraws: 1329 --- --- --- 0 BGP state: Established Neighbor address: x.x.x.x Neighbor AS: xxxxx Neighbor ID: x.x.x.x.x Neighbor caps: refresh AS4 Session: external multihop AS4 Source address: x.x.x.x Hold timer: 161/180 Keepalive timer: 24/60 Still too much information, but we can use grep!\n$birdc \u0026#39;show protocols all bgp1\u0026#39; | grep \u0026#39;Routes\u0026#39; Routes: 510977 imported, 0 exported, 510977 preferred Better, but those fields are nicely tabbed so awk to the rescue! I’d like to get that route count. Easily done.\n$birdc \u0026#39;show protocols all bgp1\u0026#39; | grep \u0026#39;Routes\u0026#39; | awk {\u0026#39;print $2\u0026#39;} 510976 I can now cron a script that will pull those values once every 5 minutes and generate an XML file which you can see right here.\nFeel free to query that page and use it for your own projects. Just be aware there is NO SLA on it :)\n","permalink":"https://mellowd.co.uk/post/using-bird-to-pull-global-bgp-route-counts/","summary":"\u003cp\u003eFor an electronics project I’m working on I wanted a way to check the current global routing table every five minutes for both IPv4 and IPv6. I did not want to log into anyone else’s router or looking glass as checking every 5 minutes may be considered abuse.\u003c/p\u003e\n\u003cp\u003eSo I thought to spin up a bird routing instance. I just wanted to receive the routes, not actually install them into the kernel on my linux box. From there I’d be able to check the table size sent over.\u003c/p\u003e","title":"Using bird to pull global BGP route counts"},{"content":"At the end of my last post on Python multithreading, I said my example was not the best. Let me expand some more on this.\nWhile testing code in the previous post, I noticed that certain code was slower when multiple threads were running. Also these threads are not tied to a CPU. If we were talking about a bigger applications in which we wanted to ensure multiple threads were on different CPUs, you are in fact looking for multiprocessing.\nConsider the following code. It simple counts to 99 999, doubles the number, then prints this to the screen. At first I’ll do this as a single thread app then multithread and time them.\nSingle-thread\n#!/usr/bin/python for i in range (100000): i *= 2 print i Multi-thread\n#!/usr/bin/python import threading lock = threading.Lock() def thread_test(i): i *= 2 with lock: print i threads = [] for i in range (100000): t = threading.Thread(target = thread_test, args = (i,)) threads.append(t) t.start() I’ll now time and run the command. I’ll run each command three times and take the average of all three:\ntime ./single.py The single thread is able to do this in 0.411 seconds, while the multithreaded app takes a full 16.409 seconds.\nNow I’ll do a test in which multithreading will make a big difference. I have a list of 500 random urls. I want to log into each, then get them to display the page contents. Not all urls respond, and I’ve also given a three second timeout to fetching any page.\nThe single thread app is coded like so:\n#!/usr/bin/python import urllib2 with open(\u0026#34;urls.txt\u0026#34;, \u0026#34;r\u0026#34;) as f: urls = f.readlines() for url in urls: request = urllib2.Request(url) try: response = urllib2.urlopen(request, timeout = 3) page = response.read() print page except: print \u0026#34;Unable to download\u0026#34; This takes a full 11 minutes and 40 seconds to fully run.\nConverted to multithread:\n#!/usr/bin/python import urllib2 import threading lock = threading.Lock() def thread_test(url): try: response = urllib2.urlopen(url, timeout = 3) page = response.read() with lock: print page except: with lock: print \u0026#34;Unable to download\u0026#34; with open(\u0026#34;urls.txt\u0026#34;, \u0026#34;r\u0026#34;) as f: urls = f.readlines() threads = [] for url in urls: request = urllib2.Request(url) t = threading.Thread(target = thread_test, args = (request,)) threads.append(t) t.start() This time the average over 3 runs is only 1 minute and 40 seconds.\nI am however still locking output to the screen. This may be bad practice, but let’s assume I don’t really care about visible output. Maybe I just want to throw some commands somewhere, or something simple like ping. If I didn’t lock before printing, how quickly could this actually run?\n#!/usr/bin/python import urllib2 import threading lock = threading.Lock() def thread_test(url): try: response = urllib2.urlopen(url, timeout = 3) page = response.read() except: pass with open(\u0026#34;urls.txt\u0026#34;, \u0026#34;r\u0026#34;) as f: urls = f.readlines() threads = [] for url in urls: request = urllib2.Request(url) t = threading.Thread(target = thread_test, args = (request,)) threads.append(t) t.start() This completes in ***1 minute and 13 seconds. *** Not as much as I hoped for. But it does mean one thing. Python is not running ALL the threads at exactly the same time. If that was the case, the max run time would be just over three seconds as that’s what the timeout is.\nI’ll load up Wireshark and run the test again. I should see how many threads are sending HTTP GETs at the same time. When I start the threads, I can see 26 threads all starting within a second of each other. Only a full 7 seconds later do others start:\nAfter that, I see more threads being added as others end. The timing seems random later as each page has a different response time.\nThis seems to be an OS imposed limit.\nConclusions Multithreading in Python has certain benefits only in specific cases. Mainly if you are requesting data from many different sources. No need to query them one at a time. Python’s initial single thread is rather efficient all by itself. I’m certainly not going to rewrite all my current code to use multithreading. Going back to my original OSPF checker, it certainly would be good to check pull information off multiple devices at the same time, but the rest of the app I’d still keep as a single thread.\n","permalink":"https://mellowd.co.uk/post/when-and-when-not-to-multithread/","summary":"\u003cp\u003e\u003ca href=\"https://mellowd.co.uk/ccie/?p=5807\"\u003eAt the end of my last post on Python multithreading, I said my example was not the best.\u003c/a\u003e Let me expand some more on this.\u003c/p\u003e\n\u003cp\u003eWhile testing code in the \u003ca href=\"https://mellowd.co.uk/ccie/?p=5807\"\u003eprevious post\u003c/a\u003e, I noticed that certain code was slower when multiple threads were running. Also these threads are not tied to a CPU. If we were talking about a bigger applications in which we wanted to ensure multiple threads were on different CPUs, \u003ca href=\"http://blog.programster.org/2014/12/14/python-multithreading-you-could-be-wasting-time/\"\u003eyou are in fact looking for multiprocessing.\u003c/a\u003e\u003c/p\u003e","title":"When and when not to multithread"},{"content":"The first ‘proper’ Python app I made logged onto a list of devices and pulled out OSPF state. This worked perfectly fine. The app correctly works out whether it can log into a device or not, and waits a few seconds to ensure a device actually responds.\nThe issue is that if I have a list of say 1000 devices, and 500 of them don’t respond, the amount of time you need to wait rapidly increases as it looks at each one in turn. Would it not be better for the app to be able to log into multiple devices at the same time in parallel? This would drastically reduce the runtime.\nBasic Threading Consider the following code:\n#!/usr/bin/python import threading def thread_test(): print \u0026#34;I am a thread\u0026#34; return threads = [] for i in range(4): t = threading.Thread(target = thread_test) threads.append(t) t.start() A module is defined called thread_test. I then spawn four threads, each of which run the module. I should therefore see four lines printed:\n$ ./thread.py I am a thread I am a thread I am a thread I am a thread Of course getting them all to do exactly the same thing is a bit boring. I may have a list of items I want to print. Let’s pass each item as an argument and print them out:\n#!/usr/bin/python import threading list_of_items = [\u0026#34;cat\u0026#34;, \u0026#34;banana\u0026#34;, \u0026#34;house\u0026#34;, \u0026#34;phone\u0026#34;] def thread_test(item): print \u0026#34;I am a \u0026#34; + item return threads = [] for word in list_of_items: t = threading.Thread(target = thread_test, args = (word,)) threads.append(t) t.start() $ ./thread.py I am a cat I am a banana I am a house I am a phone If you’ve run this code, you may notice that sometimes your output gets a bit garbled:\n$ ./thread.py I am a catI am a banana I am a house I am a phone $ ./thread.py I am a cat I am a banana I am a house I am a phone All four threads are trying to write to the screen at the same time. If outputting to the screen, or writing to a file, this output can look rather messy. Especially as the device and thread count goes up.\nLocks I can use locks to prevent this. Each thread can go do it’s business, but if I need to write to the screen or write to a file, I ensure only a single thread can do this at a time. As an example I’ll iterate through a list of 100. All those threads will create their data in memory at pretty much the same time, but I’ll ensure only one at a time can print and write to a file. I’ll also ensure that the application closes the file only after all threads are completed.\n#!/usr/bin/python import threading lock = threading.Lock() def thread_test(num): phrase = \u0026#34;I am number \u0026#34; + str(num) lock.acquire() print phrase f.write(phrase + \u0026#34;\\n\u0026#34;) lock.release() threads = [] f = open(\u0026#34;text.txt\u0026#34;, \u0026#39;w\u0026#39;) for i in range (100): t = threading.Thread(target = thread_test, args = (i,)) threads.append(t) t.start() while threading.activeCount() \u0026gt; 1: pass else: f.close() Any code between the locks acquiring and releasing can only be done one at a time. The example above doesn’t show a great example, but the action of getting data and waiting from a remote device can take a few seconds. If that can all be done at the same time, then results written once at a time to a file, it would speed things up immensely.\nUpdate – 15/12/14 Ben Cardy below mentioned a great shortcut that most viewers might miss if not reading all the comments. For that reason I’ll put it up here. My code above acquires and releases a lock when needed. There is a simpler way to do this. If you code with lock, any code indented after will essentially be wrapped in lock codes. This is nice as you don’t have to remember to release the lock. Another benefit is that the with code will release the lock even if the thread throws an exception.\nThe last code above could be rewritten like so:\n#!/usr/bin/python import threading lock = threading.Lock() def thread_test(num): phrase = \u0026#34;I am number \u0026#34; + str(num) with lock: print phrase f.write(phrase + \u0026#34;\\n\u0026#34;) threads = [] f = open(\u0026#34;text.txt\u0026#34;, \u0026#39;w\u0026#39;) for i in range (100): t = threading.Thread(target = thread_test, args = (i,)) threads.append(t) t.start() while threading.activeCount() \u0026gt; 1: pass else: f.close() ","permalink":"https://mellowd.co.uk/post/basic-python-multithreading/","summary":"\u003cp\u003eThe first ‘proper’ Python app I made logged onto a list of devices and pulled out OSPF state. This worked perfectly fine. The app correctly works out whether it can log into a device or not, and waits a few seconds to ensure a device actually responds.\u003c/p\u003e\n\u003cp\u003eThe issue is that if I have a list of say 1000 devices, and 500 of them don’t respond, the amount of time you need to wait rapidly increases as it looks at each one in turn. Would it not be better for the app to be able to log into multiple devices at the same time in parallel? This would drastically reduce the runtime.\u003c/p\u003e","title":"Basic Python Multithreading"},{"content":"I had a specific requirement recently and I wanted to test it’s behaviour. In particular the feature is DHCP snooping. Let’s quickly go over the DHCP process at a high level to see how it works:\nDHCP Let’s take the following simple diagram to show what’s going on. We have a switch with two hosts connected. We also have a DHCP server. I’m using generic names as I’ll be testing this on different switches. Assume all devices are in the same vlan.\nHost1 has just booted and needs an IP address. It’ll send a DHCP DISCOVER packet which is a broadcast. This broadcast gets sent to all ports in the vlan:\nThe DHCP server will then send an DHCP OFFER to host 1. It does this via unicast using the destination MAC as the layer 2 destination:\nHost1 then sends a DHCP REQUEST via broadcast. Why broadcast? This is because it may have received offers from multiple DHCP servers and is essentially telling all of them that they are accepting an offer from one of them.\nFinally, the DHCP server acknowledges that Host1 has accepted its offered IP with a DHCP ACK (unicast).\nNow, depending on bootp options, the offer and/or ack might actually be broadcast. The behaviour is also slightly different when using DHCP helpers, but we are mainly concerned with the DHCPDISCOVER and DHCPREQUEST packets which are always broadcast.\nDHCP Snooping In the above example, there was nothing stopping Host 2 from providing IP addresses via DHCP. This might either be malicious activity, or merely someone doing something wrong and either configuring a device wrong, or plugging in a device which should not be there.\nDHCP snooping was created to prevent this from happening. DHCP’s main concern is making sure that DHCPOFFERS only come in via trusted ports. In our example port 1 connected to the DHCP server should be a trusted port. Port2 and port 3 connected to Host 1 and Host 2 respectively should never have DHCPOFFER packets on ingress. But here is the kicker. A DHCPOFFER is in response to an event. That event is a DHCPDISCOVER. That DHCPDISCOVER is a broadcast.\nIt stands to reason that if a DHCPOFFER cannot ever ingress port2 and port3, those ports should never have DHCPDISCOVER packets replicated to them to begin with, regardless of whether those packets are broadcast. All other broadcasts should go through, but these specific DHCP ones should not.\nSo is this what we actually see in the real world? I’ll test this on the devices I have available to see what behaviour I see.\nCisco Catalyst IOS My config is as follows:\nip dhcp snooping vlan 1-4094 ip dhcp snooping interface FastEthernet0/1 switchport access vlan 10 switchport mode access ! interface FastEthernet0/2 switchport access vlan 10 switchport mode access ! interface FastEthernet0/24 switchport access vlan 10 switchport mode access ip dhcp snooping trust DHCP snooping enabled with fa0/24 being the trusted port going towards my server.\nI have host1 and host2 connected with the following MAC addresses:\n78:2b:cb:e4:e3:88 00:26:5a:ef:85:33 I’ll now listen on fa0/24. I should see both DHCPDISCOVER broadcasts coming though:\n$ sudo tcpdump -i eth1 -n port 67 and port 68 tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on eth1, link-type EN10MB (Ethernet), capture size 65535 bytes 11:45:45.204815 IP 0.0.0.0.68 \u0026gt; 255.255.255.255.67: BOOTP/DHCP, Request from 78:2b:cb:e4:e3:88, length 300 11:45:48.733826 IP 0.0.0.0.68 \u0026gt; 255.255.255.255.67: BOOTP/DHCP, Request from 00:26:5a:ef:85:33, length 300 That’s exactly what I see.\nIf I now move the capture point over to fa0/2, I hope to see no broadcasts at all. If not, this would mean the device is not replicating those broadcasts out untrusted ports:\n$ sudo tcpdump -i eth1 -n port 67 and port 68 tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on eth1, link-type EN10MB (Ethernet), capture size 65535 bytes Silence. That’s just what I wanted to see.\nJuniper EX Config is as follows:\nroot@ex2200-12\u0026gt; show configuration interfaces ge-0/0/2 unit 0 { family ethernet-switching { port-mode access; vlan { members vlan_test; } } } {master:0} root@ex2200-12\u0026gt; show configuration interfaces ge-0/0/3 unit 0 { family ethernet-switching { port-mode access; vlan { members vlan_test; } } } {master:0} root@ex2200-12\u0026gt; show configuration interfaces ge-0/0/4 unit 0 { family ethernet-switching { port-mode access; vlan { members vlan_test; } } } root@ex2200-12\u0026gt; show configuration ethernet-switching-options secure-access-port { interface ge-0/0/4.0 { dhcp-trusted; } vlan all { examine-dhcp; } } ge-0/0/4 is now my trusted DHCP server port. If I listen on that port, I should see both devices broadcasts:\n$ sudo tcpdump -i eth1 -n port 67 and port 68 tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on eth1, link-type EN10MB (Ethernet), capture size 65535 bytes 11:58:02.539119 IP 0.0.0.0.68 \u0026gt; 255.255.255.255.67: BOOTP/DHCP, Request from 78:2b:cb:e4:e3:88, length 300 11:58:05.809947 IP 0.0.0.0.68 \u0026gt; 255.255.255.255.67: BOOTP/DHCP, Request from 00:26:5a:ef:85:33, length 300 What about when listening on the untrusted port?\n$ sudo tcpdump -i eth1 -n port 67 and port 68 tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on eth1, link-type EN10MB (Ethernet), capture size 65535 bytes 11:58:55.342651 IP 0.0.0.0.68 \u0026gt; 255.255.255.255.67: BOOTP/DHCP, Request from 78:2b:cb:e4:e3:88, length 300 I hear the broadcast come through. There is only one MAC as I’ve had to disconnect the host in order to listen via wireshark.\nConclusions IOS switches filter the initial DHCPDISCOVER broadcast packets. Junos switches do not. Both devices DO drop DHCPOFFER packets coming in on untrusted ports. Cisco is a bit more intelligent in it’s behaviour. Not filtering the broadcast initially doesn’t break DHCP snooping. But it’s completely unnecessary. Why send a request out a port that you would filter a reply? I’ve seen switches reload and suddenly all devices on the switch try to get their IPs back. All devices receive all these broadcasts when only the trusted port should receive it. Filtering ensures less broadcasts on the network and also prevents badly configured devices from replying to a packet it should never have received.\n","permalink":"https://mellowd.co.uk/post/dhcp-snooping-filter-those-broadcasts/","summary":"\u003cp\u003eI had a specific requirement recently and I wanted to test it’s behaviour. In particular the feature is DHCP snooping. Let’s quickly go over the DHCP process at a high level to see how it works:\u003c/p\u003e\n\u003ch2 id=\"dhcp\"\u003eDHCP\u003c/h2\u003e\n\u003cp\u003eLet’s take the following simple diagram to show what’s going on. We have a switch with two hosts connected. We also have a DHCP server. I’m using generic names as I’ll be testing this on different switches. Assume all devices are in the same vlan.\u003c/p\u003e","title":"DHCP Snooping – Filter those broadcasts!"},{"content":"I usually have access to an ESX box at work where I can run multiple VMs and virtual routers for labbing and testing. I’ve also wanted one at home. It’s nice to be able to quickly spin up VMs when needed without always running them through my laptop.\nWhile virtual routers don’t need lots of resources, I did want a beefy machine as there are a few servers I’d like to get running that need lots of CPU power.\nRequirements 32GB RAM capable with ECC Fast CPU with at least 4 physical cores Quiet Small OOB (ilo/IPMI/Etc) Low power Specifically these are the things I don’t need:\nOptical drive Hard drive GPU The point of the box is to sit in the corner with only power and network connected. If anything went wrong, I don’t want to have to connect a monitor to it. I’m also not running any tasks requiring video output on the server itself. All VMs will be logged in via SSH.\nI already have a Synology DS411 which will provide an iSCSI connection to the ESX server. Hence no need for internal hard drives.\nMy initial build was going to be built around an Intel i7 4790. However the i7 doesn’t support ECC ram and it also has a built-in HD4000 GPU which I don’t need.\nParts list I ended up going for the Intel Xeon E3-1230v3. 4 cores, 8 threads, all the virtulisation support I need. It has no built-in GPU. It supports up to 32GB ECC RAM. Intel have released a newer 1231v3, but I couldn’t find a good price for it in the UK and all it gives is an extra 100MHz which I’m not fussed about.\nRAM is quite pricey at the moment. While I wanted 32GB, I’ll start with 16GB for now and add another 16GB when prices drop.\nFor the motherboard, I went with the SuperMicro X10SLL-F. It supports both the CPU and RAM and also has built-in IPMI. The board has two onboard Intel NICs, i217LM and i210AT. The board also has an on-board VGA card. I’m not going to use that, but it will be handy if I can’t log into both the server and IPMI. It also has an a-type USB slot on board which is quite handy as you’ll see later.\nFor the PSU, I wanted both silent and efficient. I don’t need a huge amount of wattage either as I have no GPU. I ended up with the Seasonic G-360. 80-Plus certified and very quiet.\nPart of the problem with an ESXi whitebox is ensuring that VMWare recognises all your components. I did extensive research in order to ensure this was the case. There are a couple of things I hit upon, but they were easily fixed.\nFinal part list:\nIntel Xeon E3-1230 V3 SuperMicro X10SLL-F 2 X 8GB Crucial DDR3 PC3-12800 ECC Unbuffered Seasonic G-360 PSU Aerocool Dead Silence Gaming Cube Case Old 1Gb USB flash drive Building and installing The SuperMicro board has a dedicated IPMI port and so I can do the entire install remotely. I’ll mount the ISO over the network, and all do all the config this way.\nI decided to install Vmware itself on a USB stick. What’s nice about this motherboard is that it has a USB port on the motherboard itself, meaning no external USB key required. This keeps it a bit neater.\nThe SuperMicro has two external NICs, one Intel 217 and an Intel 210. I’ve installed VMware 5.5 update 2 and the I210 Intel card is supported out of the box. No need to hack any drivers into the ISO. I’m more than happy with one NIC for now so I’ve no need to try and get the 217 working.\nOnce VMWare was installed, I created a 300GB iSCSI LUN from my Synology and attached VMWare to that. The install and set up really was painless.\nVmware shows my system as:\nVirtual devices I wanted to start a basic lab, so I have multiple virtual routers and servers running in the lab.\nWith all my VMs running, I see hardly any CPU and quite a bit of RAM usage as I expected:\nFor now the RAM amount is fine. As I ramp up the lab and prices drop, I’ll add another 16GB to the system.\nPower usage As I wanted this to be low power, I’ve done full wattage readings on power usage.\nServer off, IPMI on – 3.7 Watts Server on, no VMs running – 23 Watts Server on, all lab VMs running – 34 Watts Not at all bad. In another post I’ll show the Synology power draw as well as the power draw if all VMs are using full CPU. I’ll also go over how I automate my VMs starting and shutting down.\n","permalink":"https://mellowd.co.uk/post/esxi-whitebox-server/","summary":"\u003cp\u003eI usually have access to an ESX box at work where I can run multiple VMs and virtual routers for labbing and testing. I’ve also wanted one at home. It’s nice to be able to quickly spin up VMs when needed without always running them through my laptop.\u003c/p\u003e\n\u003cp\u003eWhile virtual routers don’t need lots of resources, I did want a beefy machine as there are a few servers I’d like to get running that need lots of CPU power.\u003c/p\u003e","title":"ESXi whitebox server"},{"content":"I’ve just started with object oriented programming in Python so I thought I’d cover some of the basics here. Please don’t assume this is a thorough tutorial on OOP!\nThe beauty of OOP is that it allows me to create a template with which I can create objects. The building blocks of the object sit in the class, while the object itself is created from that blueprint with various properties. I can then make as many of these objects as I desire from that single class. I can see this being very beneficial for certain types of programming (games especially)\nBasics I’ll start with something simple. I want to create a class called Ball. This class requires certain properties like radius and colour. I’ll create a class like so:\nclass Ball: def __init__(self, radius, colour): self.radius = radius self.colour = colour I’ve created a class called ‘Ball’ – This requires two variables, radius and colour. Note that ‘self’ refers to the object that is being created. Once this is done, I can then call this class to create objects. I need to ensure I pass both variables otherwise:\n\u0026gt;\u0026gt;\u0026gt;blueball = Ball(\u0026#34;Blue\u0026#34;) Traceback (most recent call last): File \u0026#34;\u0026#34;, line 1, in blueball = Ball(\u0026#34;Blue\u0026#34;) TypeError: __init__() takes exactly 3 arguments (2 given) Let’s do it properly:\n\u0026gt;\u0026gt;\u0026gt;blueball = Ball(10, \u0026#34;Blue\u0026#34;) blueball is now an object created from the class:\n\u0026gt;\u0026gt;\u0026gt; blueball \u0026lt;__main__.Ball instance at 0x10f11bdd0\u0026gt; \u0026gt;\u0026gt;\u0026gt; print type(blueball) type \u0026#39;instance\u0026#39; Each self.x in the class is a method which I can set and interrogate. If I wanted to see the current radius of blueball, I simply call the method I defined:\n\u0026gt;\u0026gt;\u0026gt; blueball.radius 10 I can also change the variable later if I so choose:\n\u0026gt;\u0026gt;\u0026gt; blueball.radius = 20 \u0026gt;\u0026gt;\u0026gt; blueball.radius 20 If I simply print blueball, I won’t get much:\n\u0026gt;\u0026gt;\u0026gt; print blueball \u0026lt;__main__.Ball instance at 0x10f11bdd0\u0026gt; In order to be able to get string output from an object, I need to ensure the class has a string method. I’ll add the following to my original class:\nclass Ball: def __init__(self, radius, colour): self.radius = radius self.colour = colour def __str__(self): return \u0026#34;I am a \u0026#34; + self.colour + \u0026#34; ball, with a radius of \u0026#34; + str(self.radius) Note that my old blueball variable has still been created from the old class so I’ll simply create a new one:\n\u0026gt;\u0026gt;\u0026gt; blueball = Ball(10, \u0026#34;Blue\u0026#34;) \u0026gt;\u0026gt;\u0026gt; print str(blueball) I am a Blue ball, with a radius of 10 I can now happily create as many objects as I want, with different properties. Each object is a separate instance:\n\u0026gt;\u0026gt;\u0026gt; redball = Ball(20, \u0026#34;Red\u0026#34;) \u0026gt;\u0026gt;\u0026gt; print str(redball) I am a Red ball, with a radius of 20 Let’s take this a step further. For this I’m going to use Scott Rixner’s CodeSkulptor as it has some nice draw capabilties built-in. It also runs in your browser directly.\nI’d like to create an empty space with nothing. I then want to click a button to create a new ball with random properties. That ball should then be displayed inside the space. Each click should be a new object created from my original class. I’m going to add a few more properties to my class which will come clear later.\nI’ll first get my global variables set:\nlist_of_balls = [] width = 1000 height = 600 colours = [\u0026#34;Aqua\u0026#34;,\u0026#34;Blue\u0026#34;,\u0026#34;Green\u0026#34;,\u0026#34;Lime\u0026#34;,\u0026#34;Maroon\u0026#34;,\u0026#34;Navy\u0026#34;,\u0026#34;Orange\u0026#34;,\u0026#34;Red\u0026#34;,\u0026#34;White\u0026#34;,\u0026#34;Yellow\u0026#34;] Now comes the Ball class:\nclass Ball: def __init__(self, radius, mass, colour, x_location): self.radius = radius self.mass = mass self.colour = colour self.location = [x_location, height/2] When I click the mouse button, I want certain random properties set for the object:\ndef click(): radius = random.randint(1,40) mass = radius colour = random.choice(colours) x_location = random.randint(20, width-20) new_ball = Ball(radius, mass, colour, x_location) list_of_balls.append(new_ball) The mouse click handler creates a new ball with random properties, then appends that ball to a list of balls. Each time I click a new ball is added to the list.\nI now need to draw the balls. I need to iterate through my list of objects and draw each one:\ndef draw(canvas): for ball in list_of_balls: canvas.draw_circle((ball.location[0],ball.location[1]), ball.radius, 1, ball.colour, ball.colour) Click here to view and run the code in CodeSkulptor. Press play in the top left corner to run the code.\nDynamic So the above code simply creates a bunch of balls on random places on the x axis while being in the middle of the y axis. Let’s start moving these balls around based on their initial mass. Currently the bigger the ball, the bigger the mass. Let’s keep it that way for now. All balls will simply fall towards the ground. I’d like to make sure that when the ball hits the bottom of the screem it’s reflected back up. Same goes for the top of the screen.\nFirst I need to add velocity to the object. Note that properties of an object don’t all have to be variables. I could set the velocity of all the balls exactly the same. For now I’ll use the mass of the ball:\nclass Ball: def __init__(self, radius, mass, colour, x_location): self.radius = radius self.mass = mass self.velocity = self.mass self.colour = colour self.location = [x_location, height/2] I then need to update my draw handler so it updates the position of each ball. If the y location of the ball hits the top or bottom of the screen, reverse the direction:\ndef draw(canvas): for ball in list_of_balls: ball.location[1] += ball.velocity if ball.location[1] \u0026gt;= (height - ball.radius) or ball.location[1] \u0026lt;= ball.radius: ball.velocity = -ball.velocity canvas.draw_circle((ball.location[0],ball.location[1]), ball.radius, 1, ball.colour, ball.colour) Click here to open my code in CodeSkulptor. Now every time you add a new ball, it’ll start bouncing against the top and bottom wall. Create as many balls as you like!\nConclusions It’s still early days in my OOP work. I can see this method is perfect for applications like gaming. I’m not 100% sure if I’ll have an application for it in my type of coding, I’ll have to see.\nIn the interim, the basics of OOP isn’t that difficult. I’ve still got a long way to go but happy so far!\nFor future work on my code above, it would be trivial to set a random mass. It would also be nice to extend the balls to simply bounce like in real life, or gets the balls to bounce off each other. Either way, the properties of each ball itself is independent of the environment in which it sits.\n","permalink":"https://mellowd.co.uk/post/basic-oop-python/","summary":"\u003cp\u003eI’ve just started with object oriented programming in Python so I thought I’d cover some of the basics here. Please don’t assume this is a thorough tutorial on OOP!\u003c/p\u003e\n\u003cp\u003eThe beauty of OOP is that it allows me to create a template with which I can create objects. The building blocks of the object sit in the class, while the object itself is created from that blueprint with various properties. I can then make as many of these objects as I desire from that single class. I can see this being very beneficial for certain types of programming (games especially)\u003c/p\u003e","title":"Basic OOP Python"},{"content":"I was forced to use a 3750G as a router yesterday for a WAN link that was only 70Mb. The LAN interfaces were all gig. The customer wanted to ensure that 30% of the bandwidth was available for EF marked packets. Everything else was to get 70%\nA lot of people have trouble with QoS on the 3750. This is mainly due to the tiny buffers, complexity, and the defaults it uses.\nLet’s use the following network for this post: The laptop on the left is connected on a gig port running iperf on linux. The laptop on the right is connected to a hard-coded 100Mb port. However the link itself needs to act like a 70Mb port as the carrier is policing it to 70Mb.\nBefore we turn any QoS on, let’s get a benchmark. I’m going to send 5 session from the iperf server with DSCP 0 and 5 session with DSCP EF:\nServer $ iperf -c 37.46.204.2 -w 128k -t 600 -i 5 --tos 0 -P 5 $ iperf -c 37.46.204.2 -w 128k -t 600 -i 5 --tos 184 -P 5 Both outputs show bandwidth used is 50/50:\nDSCP 0 [SUM] 0.0- 5.0 sec 28.9 MBytes 48.5 Mbits/sec DSCP EF [SUM] 5.0-10.0 sec 29.1 MBytes 48.8 Mbits/sec Also to note is the output drops on gi1/0/15. Remember we are going from a gig interface to a 100Mb interface. This is after 30 seconds:\nQOS_TEST#sh int gi1/0/15 | include drops Input queue: 0/75/0/0 (size/max/drops/flushes); Total output drops: 2145 MLS QoS on I’ll now simply turn QoS on and nothing else:\nQOS_TEST(config)#mls qos QOS_TEST(config)#end Running the same iperf commands above I see this:\nDSCP 0 retest [SUM] 0.0- 5.0 sec 55.6 MBytes 93.3 Mbits/sec DSCP EF retest [SUM] 5.0-10.0 sec 2.55 MBytes 4.27 Mbits/sec Voice packets are only getting 4% of the interface speed. Why is this? This is a default on the 3750 and you’ll need to do a little digging. First we need to see which queue EF packets will get into:\nQOS_TEST#sh mls qos maps dscp-output-q Dscp-outputq-threshold map: d1 :d2 0 1 2 3 4 5 6 7 8 9 ------------------------------------------------------------ 0 : 02-01 02-01 02-01 02-01 02-01 02-01 02-01 02-01 02-01 02-01 1 : 02-01 02-01 02-01 02-01 02-01 02-01 03-01 03-01 03-01 03-01 2 : 03-01 03-01 03-01 03-01 03-01 03-01 03-01 03-01 03-01 03-01 3 : 03-01 03-01 04-01 04-01 04-01 04-01 04-01 04-01 04-01 04-01 4 : 01-01 01-01 01-01 01-01 01-01 01-01 01-01 01-01 04-01 04-01 5 : 04-01 04-01 04-01 04-01 04-01 04-01 04-01 04-01 04-01 04-01 6 : 04-01 04-01 04-01 04-01 It’s a bit cryptic, but we can see that DSCP value 46 will map to queue 1, while DSCP 0 maps to queue 2. Let’s now check the default queueing structure on our interface:\nQOS_TEST#sh mls qos interface gi1/0/15 queueing GigabitEthernet1/0/15 Egress Priority Queue : disabled Shaped queue weights (absolute) : 25 0 0 0 Shared queue weights : 25 25 25 25 The port bandwidth limit : 100 (Operational Bandwidth:100.0) The port is mapped to qset : 1 Shaped queue weights shows 25 0 0 0. This actually means that queue 1 is used 1/25 of the interface speed. 100/25 = 4. This is why we are seeing 4Mb for EF traffic.\nAter 30 seconds I took a new reading of the drops and we see this:\nQOS_TEST#sh int gi1/0/15 | include drops Input queue: 0/75/0/0 (size/max/drops/flushes); Total output drops: 8022 A lot worse.\nThe fix The first thing we need to do is remove the shaping off the interface:\nQOS_TEST(config-if)#srr-queue bandwidth shape 0 0 0 0 Now I want to give 30% to EF and 70% to BE. I don’t want these to be hard-policed so I use the share command:\nsrr-queue bandwidth share 30 70 1 1 The share command allows other queues to use the bandwidth if those queues are not full. These numbers are not 1/x like the shape command. Rather IOS will add all the values up (102 in our case) and then give 102/30′s worth of bandwidth to queue 1.\nThis is all great for 100Mb, but remember our link is getting policed to 70Mb. So we need to add this:\nsrr-queue bandwidth limit 70 Let’s verify:\nQOS_TEST#sh mls qos interface gi1/0/15 queueing GigabitEthernet1/0/15 Egress Priority Queue : disabled Shaped queue weights (absolute) : 0 0 0 0 Shared queue weights : 30 70 1 1 The port bandwidth limit : 70 (Operational Bandwidth:70.38) The port is mapped to qset : 1 DSCP 0 final [SUM] 10.0-15.0 sec 29.0 MBytes 48.7 Mbits/sec DSCP EF final [SUM] 10.0-15.0 sec 11.2 MBytes 18.9 Mbits/sec Note too that if I just send EF or BE packets, each can use up to 70Mb. It’s only if both are sending for a total over 70Mb do they get their shares as above.\nOne issue that still remains is that I’m getting these drops after 30 seconds:\nQOS_TEST#sh int gi1/0/15 | include drops Input queue: 0/75/0/0 (size/max/drops/flushes); Total output drops: 5212 In order to properly tune buffers I thoroughly recommend a read through this document: https://supportforums.cisco.com/docs/DOC-8093\nOf course in the real world you should be calculating what the maximum amount of voice traffic you are going to send. You would never have ‘more’ voice traffic than if every person in your company was on an external call.\nIf I change the above test so that the server is sending 15Mb of UDP traffic marked DSCP EF, then I can see that the TCP BE traffic drops while no drops are on the EF queue:\niperf -c 37.46.204.2 -u -b 15m -p 5002 -t 5 No packets dropped in the EF stream:\n[ 3] 0.0- 5.0 sec 7.76 MBytes 13.0 Mbits/sec 1.776 ms 0/ 5532 (0%) Checking the port drop statistics on the 3750G:\nQOS_TEST#sh platform port-asic stats drop gi1/0/15 Interface Gi1/0/15 TxQueue Drop Statistics Queue 0 Weight 0 Frames 0 Weight 1 Frames 0 Weight 2 Frames 0 Queue 1 Weight 0 Frames 276 Weight 1 Frames 0 Weight 2 Frames 0 No voice packets dropped there either.\n","permalink":"https://mellowd.co.uk/post/3750g-qos-ef-be/","summary":"\u003cp\u003eI was forced to use a 3750G as a router yesterday for a WAN link that was only 70Mb. The LAN interfaces were all gig. The customer wanted to ensure that 30% of the bandwidth was available for EF marked packets. Everything else was to get 70%\u003c/p\u003e\n\u003cp\u003eA lot of people have trouble with QoS on the 3750. This is mainly due to the tiny buffers, complexity, and the defaults it uses.\u003c/p\u003e","title":"3750G QoS for EF and BE traffic"},{"content":"An ethernet physical port can only run at certain speeds. i.e. 10/100/1Gb/etc – Often customer will purchase a sublevel of bandwidth on that bearer speed. For example Customer A wants to buy 30Mb of bandwidth. You can’t run the physicla ports at 30Mb, so the ISP will have the interface run at 100Mb and police inbound at 30Mb.\nThis makes QoS jus a little more complicated. All the ratios we’ve used in the past will ratio themselves at the WAN port’s physical speed. Also the router will not know that if 40Mb of burst comes from the LAN, that the actual bandwidth is only 30Mb.\nIn this case, you need to first shape all traffic to 30Mb, and then inside that shaped queue give priory bandwidth to voice etc..\nIOS IOS uses the concept of parent/child policy maps. The parent will shape the queue, while the child policy attached will give each queue their respective bandwidths and priority.\npolicy-map PARENT class class-default shape average 30000000 service-policy CHILD ! policy-map CHILD class EF priority percent 10 police cir percent 10 conform-action transmit exceed-action drop class class-default bandwidth remaining percent 100 ! interface FastEthernet0/0 ip address 10.0.0.1 255.255.255.0 service-policy output PARENT In this policy the parent policy creates a queue with a bandwidth limit of 30Mb. Inside that policy rests another that gives EF packets 10 percent of priority bandwidth of that initial 30Mb queue. I’m also policing that queue as I don’t want the priority queue to starve other traffic. All other traffic gets 90-100% of the bandwidth, depending on how much priority traffic is in the queue at any one time.\nJunos As with most QoS topics, the following configuration is quite hardware specific. I’ve done the following on an SRX210H. Your configuration might change when doing the same sort of thing on a M/MX/DC SRX/etc so YMMV.\nCreate the schedulers:\ndarreno@JR2\u0026gt; show configuration class-of-service schedulers EF10 { transmit-rate { percent 10; exact; } } BE_REST { transmit-rate { remainder { 100; } } } Put the above schedulers into a schedule-map:\ndarreno@JR2\u0026gt; show configuration class-of-service scheduler-maps SCHEDULE { forwarding-class expedited-forwarding scheduler EF10; forwarding-class best-effort scheduler BE_REST; } Finally apply that map to the interface under class-of-service and configure the interface shape rate:\ndarreno@JR2\u0026gt; show configuration class-of-service interfaces ge-0/0/1 unit 2001 { scheduler-map SCHEDULE; shaping-rate 30m; } In order for the above to work I need to configure per-unit-scheduler on the physical interface:\ndarreno@JR2\u0026gt; show configuration interfaces ge-0/0/1 per-unit-scheduler; Verification Simple again in IOS:\nR1#sh policy-map int fa0/0 FastEthernet0/0 Service-policy output: PARENT Class-map: class-default (match-any) 106 packets, 6360 bytes 5 minute offered rate 0000 bps, drop rate 0000 bps Match: any Queueing queue limit 64 packets (queue depth/total drops/no-buffer drops) 0/0/0 (pkts output/bytes output) 106/6360 shape (average) cir 30000000, bc 120000, be 120000 target shape rate 30000000 Service-policy : CHILD queue stats for all priority classes: Queueing queue limit 64 packets (queue depth/total drops/no-buffer drops) 0/0/0 (pkts output/bytes output) 0/0 Class-map: EF (match-all) 0 packets, 0 bytes 5 minute offered rate 0000 bps, drop rate 0000 bps Match: dscp ef (46) Priority: 10% (3000 kbps), burst bytes 75000, b/w exceed drops: 0 police: cir 10 % cir 3000000 bps, bc 93750 bytes conformed 0 packets, 0 bytes; actions: transmit exceeded 0 packets, 0 bytes; actions: drop conformed 0000 bps, exceeded 0000 bps Class-map: class-default (match-any) 106 packets, 6360 bytes 5 minute offered rate 0000 bps, drop rate 0000 bps Match: any Queueing queue limit 64 packets (queue depth/total drops/no-buffer drops) 0/0/0 (pkts output/bytes output) 106/6360 bandwidth remaining 100% We can see the entire queue is 30Mb. Inside that queue EF traffic has priority bandwidth of 3000kbps (10% of 30Mb) – All other traffic has anything left up to 30Mb\nOn Junos its a bit cryptic again:\ndarreno@JR2\u0026gt; show class-of-service interface ge-0/0/1 Physical interface: ge-0/0/1, Index: 135 Queues supported: 8, Queues in use: 4 Scheduler map: , Index: 2 Congestion-notification: Disabled Logical interface: ge-0/0/1.2001, Index: 71 Shaping rate: 30000000 Object Name Type Index Scheduler-map SCHEDULE Output 2878 I wanted to do a more in-depth post on H-QoS but this SRX doesn’t support it. I don’t currently have an MX in the lab (only in the field) so hopefully soon…\n","permalink":"https://mellowd.co.uk/post/junos-ios-qos-3/","summary":"\u003cp\u003eAn ethernet physical port can only run at certain speeds. i.e. 10/100/1Gb/etc – Often customer will purchase a sublevel of bandwidth on that bearer speed. For example Customer A wants to buy 30Mb of bandwidth. You can’t run the physicla ports at 30Mb, so the ISP will have the interface run at 100Mb and police inbound at 30Mb.\u003c/p\u003e\n\u003cp\u003eThis makes QoS jus a little more complicated. All the ratios we’ve used in the past will ratio themselves at the WAN port’s physical speed. Also the router will not know that if 40Mb of burst comes from the LAN, that the actual bandwidth is only 30Mb.\u003c/p\u003e","title":"Junos and IOS QoS – Part 3 of 4 – Shaping to EVC speed with priority"},{"content":"Going back to the diagram we used in part 1. Let’s say that we want to shape certain traffic to certain bandwidths under congestion. I want EF packets to get 20Mb priority, AF31 packets to get 50Mb and whatever is left to get 30Mb. I want to enable WRED in the BE queue, and also modify the default WRED profile.\nI’m going to take the assumption that packets have already been marked correctly as shown in my first post.\nIOS IOS is very simple in it’s configuration:\npolicy-map OUTBOUND_QOS class EF priority 20000 class AF31 bandwidth 50000 class class-default random-detect dscp-based random-detect dscp 0 20 40 5 ! interface FastEthernet0/0 ip address 10.0.0.1 255.255.255.252 service-policy output OUTBOUND_QOS There are three classes in the service policy. Class EF has priority 20Mb, class AF31 has bandwidth 50Mb, and class-default has all that’s left. I’ve also set up WRED and it will start to drop packets when the queue level hits 20. One it hits 40 it’ll be dropping 20% of all packets (1/5) and any more packets will cause tail-drop.\nJunos In Junos, we first create our RED profile:\ndarreno@JR2\u0026gt; show configuration class-of-service drop-profiles relaxed { fill-level 50 drop-probability 10; fill-level 75 drop-probability 15; fill-level 95 drop-probability 20; } We then create our schedulers, which tells Junos how to treat each queue:\ndarreno@JR2\u0026gt; show configuration class-of-service schedulers EF { transmit-rate 20m; priority strict-high; } AF31 { transmit-rate 50m; } BE { transmit-rate 30m; drop-profile-map loss-priority any protocol any drop-profile relaxed; } We then create a scheduler-map, which tells Junos what traffic belongs in each queue:\ndarreno@JR2\u0026gt; show configuration class-of-service scheduler-maps OUTBOUND-QOS { forwarding-class expedited-forwarding scheduler EF; forwarding-class assured-forwarding scheduler AF31; forwarding-class best-effort scheduler BE; } Finally this is applied to the interface. Note that this happens under the class-of-service stanza and NOT the actual interface stanza:\ndarreno@JR2\u0026gt; show configuration class-of-service interfaces fe-0/0/7 { scheduler-map OUTBOUND-QOS; } Verification The best command for checking a service policy applied to an interface is show policy-map interface interface-name:\nR1#sh policy-map interface fa0/0 FastEthernet0/0 Service-policy output: OUTBOUND_QOS queue stats for all priority classes: Queueing queue limit 64 packets (queue depth/total drops/no-buffer drops) 0/0/0 (pkts output/bytes output) 0/0 Class-map: EF (match-all) 0 packets, 0 bytes 5 minute offered rate 0000 bps, drop rate 0000 bps Match: dscp ef (46) Priority: 20000 kbps, burst bytes 500000, b/w exceed drops: 0 Class-map: AF31 (match-all) 0 packets, 0 bytes 5 minute offered rate 0000 bps, drop rate 0000 bps Match: dscp af31 (26) Queueing queue limit 64 packets (queue depth/total drops/no-buffer drops) 0/0/0 (pkts output/bytes output) 0/0 bandwidth 50000 kbps Class-map: class-default (match-any) 800 packets, 48000 bytes 5 minute offered rate 0000 bps, drop rate 0000 bps Match: any queue limit 64 packets (queue depth/total drops/no-buffer drops) 0/0/0 (pkts output/bytes output) 800/48000 Exp-weight-constant: 9 (1/512) Mean queue depth: 0 packets dscp Transmitted Random drop Tail drop Minimum Maximum Mark pkts/bytes pkts/bytes pkts/bytes thresh thresh prob default 554/33240 0/0 0/0 20 40 1/5 This shows each of the queues as well as our RED profile attached to the class-default queue.\nOn Junos its a bit more cryptic. To see the bandwidth attached to each queue:\ndarreno@JR2\u0026gt; show interfaces fe-0/0/7 extensive | find \u0026#34;CoS information\u0026#34; CoS information: Direction : Output CoS transmit queue Bandwidth Buffer Priority Limit % bps % usec 0 best-effort 30 30000000 r 0 low none 1 expedited-forwarding 20 20000000 r 0 strict-high none 2 assured-forwarding 50 50000000 r 0 low none Interface transmit statistics: Disabled Logical interface fe-0/0/7.0 (Index 75) (SNMP ifIndex 520) (Generation 140) Flags: Device-Down SNMP-Traps 0x0 Encapsulation: ENET2 Traffic statistics: Input bytes : 2426330700 Output bytes : 90196588 Input packets: 1770438 Output packets: 872568 etc etc etc To see the bits in each queue:\ndarreno@JR2\u0026gt; show interfaces queue fe-0/0/7 Physical interface: fe-0/0/7, Enabled, Physical link is Down Interface index: 141, SNMP ifIndex: 519 Forwarding classes: 8 supported, 4 in use Egress queues: 8 supported, 4 in use Queue: 0, Forwarding classes: best-effort Queued: Packets : 863884 0 pps Bytes : 106578924 0 bps Transmitted: Packets : 863884 0 pps Bytes : 106578924 0 bps Tail-dropped packets : 0 0 pps RED-dropped packets : 0 0 pps Low : 0 0 pps Medium-low : 0 0 pps Medium-high : 0 0 pps High : 0 0 pps RED-dropped bytes : 0 0 bps Low : 0 0 bps Medium-low : 0 0 bps Medium-high : 0 0 bps High : 0 0 bps Queue: 1, Forwarding classes: expedited-forwarding Queued: Packets : 0 0 pps Bytes : 0 0 bps Transmitted: Packets : 0 0 pps Bytes : 0 0 bps Tail-dropped packets : 0 0 pps RED-dropped packets : 0 0 pps Low : 0 0 pps Medium-low : 0 0 pps Medium-high : 0 0 pps High : 0 0 pps RED-dropped bytes : 0 0 bps Low : 0 0 bps Medium-low : 0 0 bps Medium-high : 0 0 bps High : 0 0 bps Queue: 2, Forwarding classes: assured-forwarding Queued: Packets : 0 0 pps Bytes : 0 0 bps Transmitted: Packets : 0 0 pps Bytes : 0 0 bps Tail-dropped packets : 0 0 pps RED-dropped packets : 0 0 pps Low : 0 0 pps Medium-low : 0 0 pps Medium-high : 0 0 pps High : 0 0 pps RED-dropped bytes : 0 0 bps Low : 0 0 bps Medium-low : 0 0 bps Medium-high : 0 0 bps High : 0 0 bps Queue: 3, Forwarding classes: network-control Queued: Packets : 8684 0 pps Bytes : 451568 0 bps Transmitted: Packets : 8684 0 pps Bytes : 451568 0 bps Tail-dropped packets : 0 0 pps RED-dropped packets : 0 0 pps Low : 0 0 pps Medium-low : 0 0 pps Medium-high : 0 0 pps High : 0 0 pps RED-dropped bytes : 0 0 bps Low : 0 0 bps Medium-low : 0 0 bps Medium-high : 0 0 bps High : 0 0 bps I must admit, I much prefer the Cisco implementation of show policy-map interfaces\n","permalink":"https://mellowd.co.uk/post/junos-ios-qos-2/","summary":"\u003cp\u003eGoing back to the \u003ca href=\"https://mellowd.co.uk/post/junos-ios-qos-1/\"\u003ediagram we used in part 1\u003c/a\u003e. Let’s say that we want to shape certain traffic to certain bandwidths under congestion. I want EF packets to get 20Mb priority, AF31 packets to get 50Mb and whatever is left to get 30Mb. I want to enable WRED in the BE queue, and also modify the default WRED profile.\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"qos\" loading=\"lazy\" src=\"/images/QOS2.png\"\u003e\u003c/p\u003e\n\u003cp\u003eI’m going to take the assumption that packets have already been marked correctly as shown \u003ca href=\"https://mellowd.co.uk/post/junos-ios-qos-1/\"\u003ein my first post\u003c/a\u003e.\u003c/p\u003e","title":"Junos and IOS QoS – Part 2 of 4 – Schedulers and policy-maps"},{"content":"While the concepts of QoS on vendor platforms are similar, the actual configuration is very different. I wanted to do a few posts on the differences between Junos and IOS on the normal QoS things that I do on a day to day basis.\nFor this first post I’m going to use a very simple diagram:\nOn the LAN are hosts with soft-phones. These phones use specific ports but do not mark packets sent with DSCP EF. Our goal here is to ensure voice packets are marked. Any UDP packet with a port number of 5060 I will mark with DSCP EF.\nIOS IOS is very simple indeed. You match the kind of traffic you want in an ACL, create a service-policy using that ACL, mark the packets in that policy:\naccess-list 100 permit udp any eq 5060 any eq 5060 ! class-map match-all VOICE match access-group 100 ! policy-map MARK-TRAFFIC class VOICE set dscp ef ! interface FastEthernet0/0 ip address 10.0.0.1 255.255.255.0 service-policy input MARK-TRAFFIC Junos Junos is more complicated. Juniper call marking via matching on parts of a packet a multifield classification. Multifield classification works by matching terms in a firewall filter. The DSCP value is not directly set in the firewall filter. Rather the filter places a packet in a specific queue. It’s the queue outbound that sets the actual dscp value in the packet.\nFirst let’s create the classification I need:\ndarreno@JR2\u0026gt; show configuration class-of-service classifiers { dscp MARK-TRAFFIC { forwarding-class expedited-forwarding { loss-priority low code-points ef; } } } There is a built-in queue called expedited-forwarding. You cna rename these if you wish and add more queues. In the configuration above it states that any packet in this queue will be marked with DSCP EF.\ndarreno@JR2\u0026gt; show configuration firewall family inet { filter VOICE { term VOICE { from { protocol udp; source-port 5060; destination-port 5060; } then { forwarding-class expedited-forwarding; accept; } } term CATCH-ALL { then accept; } } } In the firewall statement, any packet that matches UDP with source and destination port equal to 5060 will be placed in the expedited-forwarding queue. As this is a firewall filter, I need to still allow the packets through. I also need a catch-all at the end otherwise any packet not matching the first statement is dropped.\nFinally the filter will be applied inbound on the LAN interface:\ndarreno@JR2\u0026gt; show configuration interfaces fe-0/0/7.0 family inet { filter { input VOICE; } address 10.2.2.1/24; } Both terms above will mark the needed packets as DSCP EF. All others will not be changed.\nCertain Juniper platforms do support the setting of the DSCP value inbound, but it seems to be very hardware specific\nUPDATE (03/09/2013) As a few have pointed out, I’m not actually marking anything here, I’m only classifying. My bad. In order to actually mark a packet you need to use rewrite rules. Junos has a few built-in, but you can make your own as well:\ndarreno@JR1\u0026gt; show class-of-service rewrite-rule Rewrite rule: dscp-default, Code point type: dscp, Index: 31 Forwarding class Loss priority Code point best-effort low 000000 best-effort high 000000 expedited-forwarding low 101110 expedited-forwarding high 101110 assured-forwarding low 001010 assured-forwarding high 001100 network-control low 110000 network-control high 111000 etc etc etc The default will ensure that EF traffic is marked 101110 which is DSCP value 46. We apply this rewrite to an interface like so:\ndarreno@JR1\u0026gt; show configuration class-of-service interfaces { ge-0/0/0 { unit 50 { rewrite-rules { dscp default; } } } } Of course you can create your own rewrite rules, but I’m just going for the easy way out above.\n","permalink":"https://mellowd.co.uk/post/junos-ios-qos-1/","summary":"\u003cp\u003eWhile the concepts of QoS on vendor platforms are similar, the actual configuration is very different. I wanted to do a few posts on the differences between Junos and IOS on the normal QoS things that I do on a day to day basis.\u003c/p\u003e\n\u003cp\u003eFor this first post I’m going to use a very simple diagram:\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"qos\" loading=\"lazy\" src=\"/images/QOS1.png\"\u003e\u003c/p\u003e\n\u003cp\u003eOn the LAN are hosts with soft-phones. These phones use specific ports but do not mark packets sent with DSCP EF. Our goal here is to ensure voice packets are marked. Any UDP packet with a port number of 5060 I will mark with DSCP EF.\u003c/p\u003e","title":"Junos and IOS QoS – Part 1 of 4 – Marking traffic"},{"content":"If you’re configuring an IOS router remotely with a chance of losing the device, most engineers might decide to do a reload in 5 before starting. If you happen to lose connection to the box after a change, the router will reload in 5 minutes erasing any unsaved changes. This works, but is less than ideal. It can take a few minutes for a box to reload. What happens if the box is looking after multiple customers as well?\nThere is a better way. Just revert the config. Using this is pretty trivial. You do need to turn on the archive command first though. If I try to do it without I get an error:\nC1921#conf t revert timer 1 %Turn config archive on before using Rollback Confirmed Change So let’s configure the archive command:\nC1921#conf t Enter configuration commands, one per line. End with CNTL/Z. C1921(config)#archive C1921(config-archive)#path usbflash0:backup-config C1921(config-archive)#end C1921#wr me Building configuration... Now let’s give it a try. Let’s create a loopback interface. I’ll not confirm the change which will cause the router to rollback the change after a minute:\nC1921#conf t revert time 1 Rollback Confirmed Change: Backing up current running config to usbflash0:backup-config-Apr-23-2013-10-57-27.899-BST-0 Enter configuration commands, one per line. End with CNTL/Z. C1921(config)#Rollback Confirmed Change: Rollback will begin in one minute. Enter \u0026#34;configure confirm\u0026#34; if you wish to keep what you\u0026#39;ve configured *Apr 23 2013 10:57:29.703 BST: %ARCHIVE_DIFF-5-ROLLBK_CNFMD_CHG_BACKUP: Backing up current running config to usbflash0:backup-config-Apr-23-2013-10-57-27.899-BST-0 *Apr 23 2013 10:57:29.703 BST: %ARCHIVE_DIFF-5-ROLLBK_CNFMD_CHG_START_ABSTIMER: User: hsoadmin: Scheduled to rollback to config usbflash0:backup-config-Apr-23-2013-10-57-27.899-BST-0 in 1 minutes *Apr 23 2013 10:57:29.707 BST: %ARCHIVE_DIFF-5-ROLLBK_CNFMD_CHG_WARNING_ABSTIMER: System will rollback to config usbflash0:backup-config-Apr-23-201 C1921(config)#3-10-57-27.899-BST-0 in one minute. Enter \u0026#34;configure confirm\u0026#34; if you wish to keep what you\u0026#39;ve configured C1921(config)#int lo50 *Apr 23 2013 10:57:41.523 BST: %LINK-3-UPDOWN: Interface Loopback50, changed state to up *Apr 23 2013 10:57:42.523 BST: %LINEPROTO-5-UPDOWN: Line protocol on Interface Loopback50, changed state to up C1921(config-if)#ip address 50.50.50.50 255.255.255.255 After one minute:\nC1921(config-if)#Rollback Confirmed Change: rolling to:usbflash0:backup-config-Apr-23-2013-10-57-27.899-BST-0 *Apr 23 2013 10:58:29.703 BST: %ARCHIVE_DIFF-5-ROLLBK_CNFMD_CHG_ROLLBACK_START: Start rolling to: usbflash0:backup-config-Apr-23-2013-10-57-27.899-BST-0 C1921(config-if)# *Apr 23 2013 10:58:29.711 BST: Rollback:Acquired Configuration lock. C1921(config-if)# !Pass 1 !List of Rollback Commands: interface Loopback50 no ip address 50.50.50.50 255.255.255.255 no interface Loopback50 end Total number of passes: 1 Rollback Done C1921(config-if)#command:exit *Apr 23 2013 10:58:33.139 BST: %LINK-5-CHANGED: Interface Loopback50, changed state to administratively down *Apr 23 2013 10:58:34.139 BST: %LINEPROTO-5-UPDOWN: Line protocol on Interface Loopback50, changed state to down If I wanted to save the change, you need to get back to your prompt and confirm the change:\nC1921#conf t revert time 1 Rollback Confirmed Change: Backing up current running config to usbflash0:backup-config-Apr-23-2013-11-00-09.903-BST-1 Enter configuration commands, one per line. End with CNTL/Z. C1921(config)#Rollback Confirmed Change: Rollback will begin in one minute. Enter \u0026#34;configure confirm\u0026#34; if you wish to keep what you\u0026#39;ve configured *Apr 23 2013 11:00:11.015 BST: %ARCHIVE_DIFF-5-ROLLBK_CNFMD_CHG_BACKUP: Backing up current running config to usbflash0:backup-config-Apr-23-2013-11-00-09.903-BST-1 *Apr 23 2013 11:00:11.015 BST: %ARCHIVE_DIFF-5-ROLLBK_CNFMD_CHG_START_ABSTIMER: User: hsoadmin: Scheduled to rollback to config usbflash0:backup-config-Apr-23-2013-11-00-09.903-BST-1 in 1 minutes *Apr 23 2013 11:00:11.019 BST: %ARCHIVE_DIFF-5-ROLLBK_CNFMD_CHG_WARNING_ABSTIMER: System will rollback to config usbflash0:backup-config-Apr-23-201 C1921(config)#3-11-00-09.903-BST-1 in one minute. Enter \u0026#34;configure confirm\u0026#34; if you wish to keep what you\u0026#39;ve configured C1921(config)#int lo50 C1921(config-if)#ip address 50.50.50.50 255.255.255.255 *Apr 23 2013 11:00:21.239 BST: %LINK-3-UPDOWN: Interface Loopback50, changed state to up *Apr 23 2013 11:00:22.239 BST: %LINEPROTO-5-UPDOWN: Line protocol on Interface Loopback50, changed state to up C1921(config-if)#exit C1921(config)# *Apr 23 2013 11:00:31.375 BST: %PARSER-5-CFGLOG_LOGGEDCMD: User:hsoadmin logged command:exit C1921(config)#exit C1921#configure confirm C1921# *Apr 23 2013 11:00:39.603 BST: %ARCHIVE_DIFF-5-ROLLBK_CNFMD_CHG_CONFIRM: User: hsoadmin: Confirm the configuration change Once confirmed, your config will stay that way.\nOriginally publish with link https://mellowd.co.uk/ccie/?p=3928\n","permalink":"https://mellowd.co.uk/post/reload-in-x/","summary":"\u003cp\u003eIf you’re configuring an IOS router remotely with a chance of losing the device, most engineers might decide to do a reload in 5 before starting. If you happen to lose connection to the box after a change, the router will reload in 5 minutes erasing any unsaved changes. This works, but is less than ideal. It can take a few minutes for a box to reload. What happens if the box is looking after multiple customers as well?\u003c/p\u003e","title":"RELOAD IN X ? Why not just revert the config instead of reloading the router?"},{"content":"A lot of people confuse the above 3 items. I’ll explain exactly what each of the 3 above items do, how you can see them, and how the routers use them to provide a L3VPN service.\nLet’s take the following topology for this post:\nHere we have 2 L3VPN customers running over our MPLS core. R5 is advertising 5.5.5.5/32. R8 is also advertising 5.5.5.5/32\nRoute Distinguisher The route distinguisher’s sole job is to keep a route unique while the PE routers advertise NLRI (Network Layer Reachability Information) to each other. If R5 and R8 both advertise 5.5.5.5/32 to R3, how will R3 advertise both of those routes to R4 while keeping them unique. The VPNV4 family itself doesn’t run in a VRF. It runs in the global routing instance and hence it needs something to distinguish a route.\nLet’s take a quick look at the vrf RD config for both customers and then the vpnv4 route for 6.6.6.6/32 in the BGP table on R3:\nR3#sh run | include ip vrf | rd ip vrf CUS1 rd 3.3.3.3:100 ip vrf CUS2 rd 3.3.3.3:200 R3#sh bgp vpnv4 unicast rd 3.3.3.3:200 6.6.6.6 BGP routing table entry for 3.3.3.3:200:6.6.6.6/32, version 106 Paths: (1 available, best #1, table CUS2) Not advertised to any peer Local, imported path from 4.4.4.4:200:6.6.6.6/32 4.4.4.4 (metric 4) from 4.4.4.4 (4.4.4.4) Origin incomplete, metric 2, localpref 100, valid, internal, best Extended Community: RT:100:200 OSPF DOMAIN ID:0x0005:0x000000030200 OSPF RT:0.0.0.0:2:0 OSPF ROUTER ID:10.0.47.4:0 mpls labels in/out nolabel/21 R3#sh bgp vpnv4 unicast rd 3.3.3.3:100 6.6.6.6 BGP routing table entry for 3.3.3.3:100:6.6.6.6/32, version 108 Paths: (1 available, best #1, table CUS1) Not advertised to any peer Local, imported path from 4.4.4.4:100:6.6.6.6/32 4.4.4.4 (metric 4) from 4.4.4.4 (4.4.4.4) Origin incomplete, metric 2, localpref 100, valid, internal, best Extended Community: RT:100:100 OSPF DOMAIN ID:0x0005:0x000000020200 OSPF RT:0.0.0.0:2:0 OSPF ROUTER ID:10.0.46.4:0 mpls labels in/out nolabel/23 You can see that R3 has 2 vpnv4 routes for 6.6.6.6/32 – 3.3.3.3:200:6.6.6.6/32 and 3.3.3.3\u0026#x1f4af;6.6.6.6/32. They are unique as one contains \u0026#x1f4af; and the other contains :200: – Note that R4 does not have to match this RD in any way. It simple needs to be able to accept 2 unique routes. This is especially important when using route reflectors as RR’s will normally only advertise the best route to it’s clients. If they were not unique, the RR would only be advertising one of these routes. The RD in no way determines what VPN a route actually belongs to.\nThat’s all the route distinguisher does. No more.\nRoute Target The route target’s job is to tell the PE routers what VPN a route actually belongs to. Let’s take a look at the target config on R3:\nR3#sh run | inc ip vrf|target ip vrf CUS1 route-target export 100:100 route-target import 100:100 ip vrf CUS2 route-target export 100:200 route-target import 100:200 When R3 receives an advertisement from R5, not only does it change the route into a vpnv4 route with the RD to make it unique, it also adds a community value to that advertisement. This is an RT value. Once this NLRI gets to R4, R4 will ensure that only routes that have a certain RT, will be placed in their respective VRF. As an example let’s have a look at the advertisements of 5.5.5.5 from R3 to R4:\nR3#sh bgp vpnv4 unicast rd 3.3.3.3:100 5.5.5.5 BGP routing table entry for 3.3.3.3:100:5.5.5.5/32, version 37 Paths: (1 available, best #1, table CUS1) Advertised to update-groups: 9 Local 10.0.35.5 from 0.0.0.0 (3.3.3.3) Origin incomplete, metric 2, localpref 100, weight 32768, valid, sourced, best Extended Community: RT:100:100 OSPF DOMAIN ID:0x0005:0x000000020200 OSPF RT:0.0.0.0:2:0 OSPF ROUTER ID:10.0.35.3:0 mpls labels in/out 24/nolabel We can see the extended community of 100:100 is encoded into this NLRI on R3. This is advertised to R4: R4#sh bgp vpnv4 unicast rd 3.3.3.3:100 5.5.5.5 BGP routing table entry for 3.3.3.3:100:5.5.5.5/32, version 178 Paths: (1 available, best #1, no table) Not advertised to any peer Local 3.3.3.3 (metric 4) from 3.3.3.3 (3.3.3.3) Origin incomplete, metric 2, localpref 100, valid, internal, best Extended Community: RT:100:100 OSPF DOMAIN ID:0x0005:0x000000020200 OSPF RT:0.0.0.0:2:0 OSPF ROUTER ID:10.0.35.3:0 mpls labels in/out nolabel/24 R4#sh run | include ip vrf | 100:100 ip vrf CUS1 route-target export 100:100 route-target import 100:100 R4 has an import 100:100 configuration under it’s VRF, and hence matching the community of 100:100 on the received NLRI, the PE router knows that the advertisement is meant for vrf CUS1. Note that the RD has nothing to do with this.\nVPN Label The VPN label is to determine what VPN a packet belongs to. But hang on, surely that’s what the RT is for? No. The RT is for the control plane, while the VPN label is for the data plane. Let’s expand on that idea a bit. When R3 advertises NLRI to R4, the RT is used to determine where a route actually belongs. When it comes to R5 actually sending a packet to R6, the VPN label is used. Why? Because when a packet is sent, there is no field in the packet that the route-target is stored. Only the route advertisement contains the route-target as a community value. When R5 sends a ping to R6 from it’s loopback, it’s simply a packet with a destination address of 6.6.6.6 and a source address of 5.5.5.5.\nSo with L3VPNs we have two labels. The top label is the transport label and the bottom label is the VPN label. PHP will pop the transport label off the second to last router, but the VPN label will only be popped by the actual PE in question. When that frame comes in with the VPN label, R6 knows which VRF that packet belongs to.\nVPN labels are advertised in the NLRI along with the RT. Let’s take a look at the 2 VPN labels that R4 is advertising to R3:\nR3#sh bgp vpnv4 unicast rd 4.4.4.4:100 6.6.6.6 BGP routing table entry for 4.4.4.4:100:6.6.6.6/32, version 6 Paths: (1 available, best #1, no table) Not advertised to any peer Local 4.4.4.4 (metric 4) from 4.4.4.4 (4.4.4.4) Origin incomplete, metric 2, localpref 100, valid, internal, best Extended Community: RT:100:100 OSPF DOMAIN ID:0x0005:0x000000020200 OSPF RT:0.0.0.0:2:0 OSPF ROUTER ID:10.0.46.4:0 mpls labels in/out nolabel/21 R3#sh bgp vpnv4 unicast rd 4.4.4.4:200 6.6.6.6 BGP routing table entry for 4.4.4.4:200:6.6.6.6/32, version 8 Paths: (1 available, best #1, no table) Not advertised to any peer Local 4.4.4.4 (metric 4) from 4.4.4.4 (4.4.4.4) Origin incomplete, metric 2, localpref 100, valid, internal, best Extended Community: RT:100:200 OSPF DOMAIN ID:0x0005:0x000000030200 OSPF RT:0.0.0.0:2:0 OSPF ROUTER ID:10.0.47.4:0 mpls labels in/out nolabel/23 We can see that R3 will use a VPN label of 21 when sending traffic to the CUS1 VRF, while it’ll use VPN label 23 when sending to CUS2′s VRF.\nLet’s run a traceroute from R5 and R8 to confirm this.\nCUS1 R5#traceroute 6.6.6.6 Type escape sequence to abort. Tracing the route to 6.6.6.6 1 10.0.35.3 36 msec * 52 msec 2 10.0.13.1 [MPLS: Labels 20/21 Exp 0] 120 msec 120 msec 132 msec 3 10.0.12.2 [MPLS: Labels 18/21 Exp 0] 92 msec 148 msec 104 msec 4 10.0.46.4 [MPLS: Label 21 Exp 0] 104 msec 100 msec 68 msec 5 10.0.46.6 172 msec * 140 msec CUS2 R8#traceroute 6.6.6.6 Type escape sequence to abort. Tracing the route to 6.6.6.6 1 10.0.38.3 44 msec 64 msec 40 msec 2 10.0.13.1 [MPLS: Labels 20/23 Exp 0] 132 msec 132 msec 88 msec 3 10.0.12.2 [MPLS: Labels 18/23 Exp 0] 124 msec 156 msec 104 msec 4 10.0.47.4 [MPLS: Label 23 Exp 0] 192 msec 96 msec 76 msec 5 10.0.47.7 156 msec * 116 msec The first hop for CUS1 shows a label stack of 20/21 – 20 being transport and 21 being the VPN. CUS2 uses 20/23 – Notice that as the egress PE is the same, the same transport label is used.\nOn the 3rd hop on both, R2 is popping off the transport label. Both frames now get to R4. One has a VPN label of 21 and the second a label of 23. R4 knows which VRF both packets belong to and sends them on their way to the correct routers.\nHopefully this helps clear this up for some of you.\nOriginally publish with link https://mellowd.co.uk/ccie/?p=2923\n","permalink":"https://mellowd.co.uk/post/rd-vs-rt-vl/","summary":"\u003cp\u003eA lot of people confuse the above 3 items. I’ll explain exactly what each of the 3 above items do, how you can see them, and how the routers use them to provide a L3VPN service.\u003c/p\u003e\n\u003cp\u003eLet’s take the following topology for this post:\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"topology of routers\" loading=\"lazy\" src=\"/images/RTRDVPN3.png\"\u003e\u003c/p\u003e\n\u003cp\u003eHere we have 2 L3VPN customers running over our MPLS core. R5 is advertising 5.5.5.5/32. R8 is also advertising 5.5.5.5/32\u003c/p\u003e\n\u003ch2 id=\"route-distinguisher\"\u003eRoute Distinguisher\u003c/h2\u003e\n\u003cp\u003eThe route distinguisher’s sole job is to keep a route unique while the PE routers advertise NLRI (Network Layer Reachability Information) to each other. If R5 and R8 both advertise 5.5.5.5/32 to R3, how will R3 advertise both of those routes to R4 while keeping them unique. The VPNV4 family itself doesn’t run in a VRF. It runs in the global routing instance and hence it needs something to distinguish a route.\u003c/p\u003e","title":"MPLS L3VPN- Route Distinguisher vs Route Target vs VPN Label"},{"content":"A couple of interesting graphs. You can see in the following diagrams how Sandy affected the BGP table. These images I got from the CIDR report\n450 AS numbers disappeared as well as roughly 3000 prefixes. Which of course gradually returned\nI don’t have figures for RTT to various sites as my guess is that a fair amount of trans-Atlantic traffic would’ve been quite higher.\nUpdate: RIPE has a good RTT graph over here\n","permalink":"https://mellowd.co.uk/post/hurricane-sandys-affect-on-the-core-bgp-table/","summary":"\u003cp\u003eA couple of interesting graphs. You can see in the following diagrams how Sandy affected the BGP table. These images I got from the \u003ca href=\"http://www.cidr-report.org/as2.0/#General_Status\"\u003eCIDR report\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e450 AS numbers disappeared as well as roughly 3000 prefixes. Which of course gradually returned\u003c/p\u003e\n\u003cp\u003eI don’t have figures for RTT to various sites as my guess is that a fair amount of trans-Atlantic traffic would’ve been quite higher.\u003c/p\u003e\n\u003cp\u003eUpdate: \u003ca href=\"http://albatross.ripe.net/demo-area/sandy-2012/\"\u003eRIPE has a good RTT graph over here\u003c/a\u003e\u003c/p\u003e","title":"Hurricane Sandy’s affect on the core BGP table"},{"content":"I know the title is quite a mouthful, but I did want to cover all the above in this post. Daniel asked me to check a few things as I have ready access to real switches.\nYou learn in your studies that layer 2 control packets are ‘special’ – Special in the way that traffic going over the trunk between 2 switches does not follow the standard practice. Let’s use wireshark to see exactly what is going on in a bunch of scenarios. It’ll also give me the opportunity to do a bit of testing with SPAN and RSPAN.\nLet’s use the basic topology:\nLet’s first set up a span session on the 3750. I will monitor port gi1/0/9 in both directions and send that traffic to gi1/0/24 to be picked up by the laptop.\nmonitor session 1 source interface Gi1/0/9 monitor session 1 destination interface Gi1/0/24 The first thing I noticed when I plug in my laptop however is that Windows of course is very noisy. Already my capture is filling up with stuff that Windows is sending out. And so I’ve downloaded an NST ISO which I’ll listen with on the laptop.\nSo now that I’ve booted up into NST and got Wireshark running, I hardly see anything at all happening between the 2 switches. Where is all the layer 2 control traffic? Well the problem is that control traffic is not automatically replicated to a SPAN port. You need to enable encapsulation replication in order for it to work. Let’s do so:\nC3750#conf t C3750(config)#monitor session 1 destination interface gi1/0/24 encapsulation replicate Let’s verify:\nC3750#sh monitor session 1 Session 1 --------- Type : Local Session Source Ports : Both : Gi1/0/9 Destination Ports : Gi1/0/24 Encapsulation : Replicate Ingress : Disabled I can now see lot’s of control traffic in my Wireshark capture.\nBoth switches are already connected to each other. By default they’ll create a trunk link and vlan 1 will be the native vlan. I’ll then configure the switches to tag the native vlan and see what happens.\nLet’s ensure the native vlan is not currently tagged:\nC3750#sh vlan dot1q tag native dot1q native vlan tagging is disabled So what does my CDP/STP/DTP control packets look like in Wireshark? Note that I’m running the default mode of STP on the switch for now\nI do see something odd. I am seeing an STP packet that has a dot1q tag of 1, 10 and an untagged packet. 10 I can understand because I have created vlan 10 and it has a separate STP instance. But why would the main one be tagged with vlan 1 if vlan 1 is the native vlan?\ndot1q vlan 1\ndot1q vlan 10\nno dot1q tag\nWhat about DTP and CDP?\nDTP\nCDP\nBoth CDP and DTP are currently sent with no vlan tag at all. CDP does carry information about the native vlan in it’s packet which is why CDP does complain when these don’t match on either end. But the important thing is that both are untagged.\nLet’s now tag the native vlan and see what happens.\nC3750(config)#vlan dot1q tag native C3750#sh vlan dot1q tag native dot1q native vlan tagging is enabled Let’s bring up the interfaces again and see what we see.\nDTP\nCDP\nInteresting. DTP has not tag, but CDP is using a tag of 1.\nWhat about STP?\nI’m seeing the same as what I saw above. I see a tagged vlan 1 STP frame, a tagged vlan 10 STP frame, and finally an untagged STP frame\nWhat happens if I change the native vlan to 10? Well no need to paste output because it’s exactly the previous example. i.e. vlan 10 is now the native vlan and tagged, but CDP is still using a tag value of 1. STP and DTP remain unchanged.\nNow let’s try something else. Let’s keep vlan 10 as the tagged native, but let’s remove vlan 1 from the trunk:\ninterface GigabitEthernet1/0/9 switchport trunk encapsulation dot1q switchport trunk native vlan 10 switchport trunk allowed vlan 2-4094 DTP is unchanged. i.e. it’s still sending untagged traffic. CDP however is sending tagged traffic. In vlan 1!\nAs a quick test I created int vlan 1 on both switches in the same subnet and tried to ping accross. I could not. Therefore it looks like Cisco will use vlan 1 tagged to send certain control data, even if vlan 1 is pruned, but no user data will be allowed on that vlan.\nFor STP I still have the same 3 outputs. A tagged vlan 1 STP frame, A tagged vlan 10 STP frame, and an untagged STP frame.\nOne last thing I wanted to test now was RSPAN config. I’ve always been a little confused as to the correct config on a switch that is the RSPAN end-point, and is also sending traffic to be monitored. i.e. Let’s say that the 3550 above is monitoring traffic on vlan 2 with a destination of remote span vlan 500. The 3750 is the rspan endpoint who monitoring rspan vlan 500 and sends it out to a local port on the switch. What happens if the 3750 is also monitoring vlan 2 on it’s own ports and sending out. Do we configure the destination to vlan 500 or straight to a local port?\nLet’s configure it like so:\nC3750#sh monitor session all Session 1 --------- Type : Remote Source Session Source Ports : Both : Gi1/0/9 Dest RSPAN VLAN : 500 Session 2 --------- Type : Remote Destination Session Source RSPAN VLAN : 500 Destination Ports : Gi1/0/24 Encapsulation : Replicate Ingress : Disabled I’ve tested sending it to RSPAN vlan 500 and I don’t see any traffic at all. As soon as I change it to send traffic directly to the port it works.\n**EDIT (05/06/12) – I’ve uploaded my captures to Cloudshark so you can take them apart to do your own research\nUntagged native vlan 1\nTagged native vlan 1\nTagged native vlan 10\nTagged native vlan 10 with vlan 1 removed from trunk**\n","permalink":"https://mellowd.co.uk/post/span-rspan-layer-2-control-packets-and-vlans/","summary":"\u003cp\u003eI know the title is quite a mouthful, but I did want to cover all the above in this post. \u003ca href=\"http://lostintransit.se/\"\u003eDaniel\u003c/a\u003e asked me to check a few things as I have ready access to real switches.\u003c/p\u003e\n\u003cp\u003eYou learn in your studies that layer 2 control packets are ‘special’ – Special in the way that traffic going over the trunk between 2 switches does not follow the standard practice. Let’s use wireshark to see exactly what is going on in a bunch of scenarios. It’ll also give me the opportunity to do a bit of testing with SPAN and RSPAN.\u003c/p\u003e","title":"SPAN, RSPAN, Layer 2 control packets and VLANS"},{"content":"I’ve noticed that a lot of people seem to get confused with what exactly dot1q is doing most of the time. It’s actually incredibly simply.\nTagging traffic, or Trunking in Cisco-talk, is a very straightforward process. I will not be discussing ISL here as not only do I not use it, but Cisco is phasing it out on their stuff anyway.\nThe dot1q tag is simply inserted into the layer2 header when a packet leaves a switchport over a trunk. If a frame leaves a switchport that is not a trunk, there is no dot1a tag inserted into it, regardless of what vlan the frame came from or is going to. This means that the following is a perfectly valid topology:\nLet’s configure the above quickly.\n3560TOP#conf t Enter configuration commands, one per line. End with CNTL/Z. 3560TOP(config)#int range fa0/1, fa0/8 3560TOP(config-if-range)#switchport mode access 3560TOP(config-if-range)#switchport access vlan 10 % Access VLAN does not exist. Creating vlan 10 C3550#conf t Enter configuration commands, one per line. End with CNTL/Z. C3550(config)#int range fa0/1, fa0/8 C3550(config-if-range)#switchport mode access C3550(config-if-range)#switchport access vlan 20 % Access VLAN does not exist. Creating vlan 20 Can the PC’s ping each other?\nPC2#ping 10.1.1.1 Type escape sequence to abort. Sending 5, 100-byte ICMP Echos to 10.1.1.1, timeout is 2 seconds: !!!!! Success rate is 100 percent (5/5), round-trip min/avg/max = 4/10/28 ms PC1#ping 10.1.1.2 Type escape sequence to abort. Sending 5, 100-byte ICMP Echos to 10.1.1.2, timeout is 2 seconds: !!!!! Success rate is 100 percent (5/5), round-trip min/avg/max = 4/4/8 ms They both can ping each other. But aren’t the devices in different vlans? They sure are, yet they can still communicate. Why is this?\nThe issue is that the switch interlink are both access ports. An access port will not send or accept tagged traffic. Hence when SW1 sends PC1′s traffic over the link, the tag is removed. When that packet comes into SW2′s fa0/8 interface, that interface is part of vlan 20. SW2 will allow that frame to flow to PC2. The same happens vice-versa.\nLet’s change the topology so that there is a trunk instead between the 2 switches. I also want to force the use of dot1q.\nC3550#conf t Enter configuration commands, one per line. End with CNTL/Z. C3550(config)#default interface fa0/8 Interface FastEthernet0/8 set to default configuration 3560TOP#conf t Enter configuration commands, one per line. End with CNTL/Z. 3560TOP(config)#default interface fa0/8 Interface FastEthernet0/8 set to default configuration 3560TOP(config-if)#switchport trunk encapsulation dot1q Now can the PC’s ping each other?\nPC2#ping 10.1.1.1 Type escape sequence to abort. Sending 5, 100-byte ICMP Echos to 10.1.1.1, timeout is 2 seconds: ..... Success rate is 0 percent (0/5) They cannot. It now works as expected SW1 now sends PC1′s frame over the link with a dot1q tag. In that tag is the vlan id of 10. When it gets to SW2, it’ll look at that tag and ensure the frame is not sent out any access port that is not in vlan 10.\nAnother important part of dot1q is the notion of a native vlan. The native vlan does not get tagged over a trunk unless you configure otherwise. Vlan1 is the default native vlan.\nSo let’s try something here. Let’s configure SW1 to think that vlan 10 is the native vlan, while on SW2 let’s change it to vlan20. Will my PC’s be able to ping each other?\n3560TOP(config)#int fa0/8 3560TOP(config-if)#switchport trunk native vlan 10 *Mar 1 00:13:42.385: %SPANTREE-2-RECV_PVID_ERR: Received BPDU with inconsistent peer vlan id 1 on FastEthernet0/8 VLAN10. *Mar 1 00:13:42.385: %SPANTREE-2-BLOCK_PVID_PEER: Blocking FastEthernet0/8 on VLAN0001. Inconsistent peer vlan. *Mar 1 00:13:42.385: %SPANTREE-2-BLOCK_PVID_LOCAL: Blocking FastEthernet0/8 on VLAN0010. Inconsistent local vlan. *Mar 1 00:14:13.389: %SPANTREE-2-UNBLOCK_CONSIST_PORT: Unblocking FastEthernet0/8 on VLAN0001. Port consistency restored. *Mar 1 00:14:19.270: %CDP-4-NATIVE_VLAN_MISMATCH: Native VLAN mismatch discovered on FastEthernet0/8 (10), with C3550 FastEthernet0/8 (20). I’ve done the same on SW2, but for vlan 20. You can see that the switch is giving me all kinds of error messages.\nHowever I cannot ping between my PCs:\nPC2#ping 10.1.1.1 Type escape sequence to abort. Sending 5, 100-byte ICMP Echos to 10.1.1.1, timeout is 2 seconds: ..... Success rate is 0 percent (0/5) I tried disabling CDP, hard coding the trunk between the 2 switches and also disabling negotiation between the 2 links but no communication at all. Even though it should work theoretically, the switches just do not like the native vlan not matching on either side of the link\nI was not happy with the outcome above, so I dug a little deeper. It seems STP is blocking communication. The BPDU’s still carry vlan information with them. What I’ve done on both switches is disable STP on vlans 10 and 20 on both switches:\n3560TOP(config)#no spanning-tree vlan 10,20 Can I now ping?\nPC2#ping 10.1.1.1 Type escape sequence to abort. Sending 5, 100-byte ICMP Echos to 10.1.1.1, timeout is 2 seconds: !!!!! Success rate is 100 percent (5/5), round-trip min/avg/max = 4/4/8 ms Indeed I can. So even though there is a trunk between the 2 switches and both devices are in separate vlans, they can still communicate as SW1 and SW2 both think that the native vlan matches on each side. They have no idea that PC1 is in vlan 10 and PC2 is in vlan 20.\nI don’t much like leaving the native vlan untagged. Thankfully we have an option to tag all frames:\n3560TOP(config)#vlan dot1q tag native C3550(config)#vlan dot1q tag native What this does is essentially ignore the native vlan setting. All traffic, regardless of vlan, will be tagged over the link. This is proven by the fact I can now no longer ping again:\nPC2#ping 10.1.1.1 Type escape sequence to abort. Sending 5, 100-byte ICMP Echos to 10.1.1.1, timeout is 2 seconds: ..... Success rate is 0 percent (0/5) It’s important to note that an untagged frame is identical to a frame that goes in and out of an access port. There is no difference. To prove this I’m going to put both PC1 and PC2 into vlan 10, and them create a trunk link to PC1. PC1 does not understand trunk links and will continue to send regular traffic. I’ll configure SW1 to untag the native vlan again and make vlan 10 the native vlan. Will this work?\n3560TOP(config)#int fa0/8 3560TOP(config-if)#no switchport trunk native vlan 10 3560TOP(config-if)#exit 3560TOP(config)#no vlan dot1q tag native 3560TOP(config)#int fa0/1 3560TOP(config-if)#switchport trunk encap dot 3560TOP(config-if)#switch mode trunk 3560TOP(config-if)#switchport trunk native vlan 10 Can PC1 ping PC2?\nPC1#ping 10.1.1.2 Type escape sequence to abort. Sending 5, 100-byte ICMP Echos to 10.1.1.2, timeout is 2 seconds: !!!!! Success rate is 100 percent (5/5), round-trip min/avg/max = 4/6/16 ms It sure can. PC1 is simply sending untagged traffic off to SW1. SW1 assumes that untagged traffic is part of vlan10 and forwards those frames throughout vlan 10. This goes over the trunk to SW2, and sends those frames over the vlan10 access ports where it gets to PC2.\nOf course routers and some servers can also send tagged traffic. Let’s change PC1 so that it sends out tagged traffic. Let’s change SW1 back to the regular native vlan on port fa0/1\n3560TOP(config)#int fa0/1 3560TOP(config-if)#no switchport trunk native vlan 10 PC1(config)#int fa0/0 PC1(config-if)#no ip address PC1(config-if)#int fa0/0.1 PC1(config-subif)#encapsulation dot1Q 10 PC1(config-subif)#ip address 10.1.1.1 255.255.255.0 Can I ping?\nPC1#ping 10.1.1.2 Type escape sequence to abort. Sending 5, 100-byte ICMP Echos to 10.1.1.2, timeout is 2 seconds: !!!!! Success rate is 100 percent (5/5), round-trip min/avg/max = 1/4/8 ms As expected I sure can.\nJust like the switches, I can also make any single vlan I choose to be the native vlan. This frame will simply be sent untagged. Let’s change SW1′s fa0/1 interface to an access port again, and change PC1 to continue to use dot1q, but ensure that vlan 10 traffic is untagged:\n3560TOP(config)#default interface fa0/1 Interface FastEthernet0/1 set to default configuration 3560TOP(config)#int fa0/1 3560TOP(config-if)#switch mode access 3560TOP(config-if)#switch access vlan 10 PC1(config)#int fa0/0.1 PC1(config-subif)#encapsulation dot1Q 10 native Ping should still work, does it?\nPC1#ping 10.1.1.2 Type escape sequence to abort. Sending 5, 100-byte ICMP Echos to 10.1.1.2, timeout is 2 seconds: !!!!! Success rate is 100 percent (5/5), round-trip min/avg/max = 4/4/8 ms As you can see, it’s pretty simple stuff at the end of the day.\nOriginally publish with link https://mellowd.co.uk/ccie/?p=1551\n","permalink":"https://mellowd.co.uk/post/protocol-fundamentals-dot1q/","summary":"\u003cp\u003eI’ve noticed that a lot of people seem to get confused with what exactly dot1q is doing most of the time. It’s actually incredibly simply.\u003c/p\u003e\n\u003cp\u003eTagging traffic, or Trunking in Cisco-talk, is a very straightforward process. I will not be discussing ISL here as not only do I not use it, but Cisco is phasing it out on their stuff anyway.\u003c/p\u003e\n\u003cp\u003eThe dot1q tag is simply inserted into the layer2 header when a packet leaves a switchport over a trunk. If a frame leaves a switchport that is not a trunk, there is no dot1a tag inserted into it, regardless of what vlan the frame came from or is going to. This means that the following is a perfectly valid topology:\u003c/p\u003e","title":"Protocol Fundamentals – dot1q"},{"content":"So now it’s time to actually build my topology. There are a number of issues I’d like to get my head around. It helps to know what the planned set up is going to be.\nI plan to use my laptop for my studies. From my laptop I’ll be connecting to my dynamips box. My dynamips box is connected to 4 switches. I need to be able to console into all 4 switches remotely, but I don’t want to buy a terminal server. I also want to be able to telnet into all the routers running on the dynamips box.\nThis is how it’ll look:\nThis is all possible of course, and I’ll be showing how I did it.\nI needed 12 ‘breakout’ ports on the system. Essentially dynamips can map emulated router ports to real ports, allowing you to connect your emulated routers to real switches. I went and bought 3 of these on ebay (Sun Quad Fast PCI Ethernet Card 501-4366):\nHowever, first issue. I can only fit 2 NIC’s in my box. The cards are long, and the 3rd simply down not fit in the box:You can see that there is a heatsinked chip in the way as these cards are very long.\nSo I went and bought 4 of these:\nBut these things are pretty awful. They are more bulky than they look, and Ubuntu just doesn’t like to see more than one of them. It’s also messy.\nI then decided to find a smaller 4 port NIC that would work. I looked around and found the Dlink DFE-580TX.\nWill this fit? It does indeed!\nI’ve downloaded and installed the latest version of Ubuntu 64bit server (At this time, 10.10)\nDoes Ubuntu see all my NICs?\n[email protected]:~$ lspci | grep Ethernet 02:00.0 Ethernet controller: Realtek Semiconductor Co., Ltd. RTL8111/8168B PCI Express Gigabit Ethernet controller (rev 03) 04:04.0 Ethernet controller: D-Link System Inc DL10050 Sundance Ethernet (rev 15) 04:05.0 Ethernet controller: D-Link System Inc DL10050 Sundance Ethernet (rev 15) 04:06.0 Ethernet controller: D-Link System Inc DL10050 Sundance Ethernet (rev 15) 04:07.0 Ethernet controller: D-Link System Inc DL10050 Sundance Ethernet (rev 15) 05:00.1 Ethernet controller: Sun Microsystems Computer Corp. Happy Meal 10/100 Ethernet [hme] (rev 01) 05:01.1 Ethernet controller: Sun Microsystems Computer Corp. Happy Meal 10/100 Ethernet [hme] (rev 01) 05:02.1 Ethernet controller: Sun Microsystems Computer Corp. Happy Meal 10/100 Ethernet [hme] (rev 01) 05:03.1 Ethernet controller: Sun Microsystems Computer Corp. Happy Meal 10/100 Ethernet [hme] (rev 01) 06:00.1 Ethernet controller: Sun Microsystems Computer Corp. Happy Meal 10/100 Ethernet [hme] (rev 01) 06:01.1 Ethernet controller: Sun Microsystems Computer Corp. Happy Meal 10/100 Ethernet [hme] (rev 01) 06:02.1 Ethernet controller: Sun Microsystems Computer Corp. Happy Meal 10/100 Ethernet [hme] (rev 01) 06:03.1 Ethernet controller: Sun Microsystems Computer Corp. Happy Meal 10/100 Ethernet [hme] (rev 01) How has Ubuntu numbered those interfaces? You can find out like this:\n[email protected]:~$ sudo vi /etc/udev/rules.d/70-persistent-net.rules # PCI device 0x10ec:0x8168 (r8169) SUBSYSTEM==\u0026#34;net\u0026#34;, ACTION==\u0026#34;add\u0026#34;, DRIVERS==\u0026#34;?*\u0026#34;, ATTR{address}==\u0026#34;00:24:21:de:ed:1e\u0026#34;, ATTR{dev_id}==\u0026#34;0x0\u0026#34;, ATTR{type}==\u0026#34;1\u0026#34;, KERNEL==\u0026#34;eth*\u0026#34;, NAME=\u0026#34;eth0\u0026#34; # PCI device 0x108e:0x1001 (hme) SUBSYSTEM==\u0026#34;net\u0026#34;, ACTION==\u0026#34;add\u0026#34;, DRIVERS==\u0026#34;?*\u0026#34;, ATTR{address}==\u0026#34;08:00:20:8d:49:19\u0026#34;, ATTR{dev_id}==\u0026#34;0x0\u0026#34;, ATTR{type}==\u0026#34;1\u0026#34;, KERNEL==\u0026#34;eth*\u0026#34;, NAME=\u0026#34;eth6\u0026#34; # PCI device 0x1186:0x1002 (sundance) SUBSYSTEM==\u0026#34;net\u0026#34;, ACTION==\u0026#34;add\u0026#34;, DRIVERS==\u0026#34;?*\u0026#34;, ATTR{address}==\u0026#34;00:0d:88:cd:4f:7a\u0026#34;, ATTR{dev_id}==\u0026#34;0x0\u0026#34;, ATTR{type}==\u0026#34;1\u0026#34;, KERNEL==\u0026#34;eth*\u0026#34;, NAME=\u0026#34;eth4\u0026#34; You can see that eth0 is my onboard NIC. eth6 is one of the Sun’s ports and eth4 is one of the D-Link’s ports.\nI need to set up my networking interfaces file so ifconfig knows that they are there. I’m going to be putting them in manual mode, and use a script to start them up when I need to run my topologies.\n[email protected]:~$ sudo vi /etc/network/interfaces # The primary network interface auto eth0 iface eth0 inet static address 10.20.30.12 netmask 255.255.255.0 gateway 10.20.30.254 # # Sun auto eth1 iface eth1 inet manual # D-Link auto eth2 iface eth2 inet manual # auto eth3 iface eth3 inet manual # auto eth4 iface eth4 inet manual # auto eth5 iface eth5 inet manual # Sun auto eth6 iface eth6 inet manual # auto eth7 iface eth7 inet manual etc.... I have no terminal server, so I’ve just done this: Why buy a terminal server when an old PC will do\nRight. So now I have my 12 NIC ports. I have 4 serial cables connected for my switches. The only thing left now is the topology itself. Dynamips allows you to breakout your emulated routers to real switches. This is simple to do in the .net file.\nUsually in a .net file, you specify that a particular router port is connected to another router port like so:\n[[Router CR1]] model = 3725 console = 2001 slot1 = NM-4T slot2 = NM-1FE-TX s1/0 = AR1 s1/0 s1/2 = AR3 s1/2 Fa0/0 = CR3 Fa0/0 Fa2/0 = CR2 Fa2/0 In the above configuration, I’m telling dynamips that R1’s S1/0 interface is connected to AR1’s S1/0 interface. R1’s Fa0/0 interface is connected to CR3’s Fa0/0 interface and so on.\nInstead of doing it that way, you could do it this way:\n[[Router CR1]] model = 3725 console = 2001 slot1 = NM-4T slot2 = NM-1FE-TX s1/0 = AR1 s1/0 s1/2 = AR3 s1/2 Fa0/0 = NIO_linux_eth:eth3 Fa0/1 = NIO_linux_eth:eth2 In the above I’m telling dynamips to map R1’s Fa0/0 interface to eth3, and Fa0/1 to eth2. This then allows me to run a cat5 cable from eth3 on the server to a real switch. It’s also important to note that you can mix and match both modes so you can get very complex topologies.\nLet’s cook up a quick bash script that will bring up my interfaces and start the dynamips process.\n[email protected]:~$ sudo vim /etc/ccie.sh #!/bin/bash #Bring interfaces up ifconfig eth1 up ifconfig eth2 up ifconfig eth3 up ifconfig eth4 up ifconfig eth5 up ifconfig eth6 up ifconfig eth7 up ifconfig eth8 up ifconfig eth9 up ifconfig eth10 up ifconfig eth11 up ifconfig eth12 up #Start Hypervisor dynamips -H 7200 \u0026amp; sudo chmod +x /etc/ccie.sh [email protected]:~$ sudo /etc/ccie.sh [email protected]:~$ Cisco Router Simulation Platform (version 0.2.8-RC2-amd64) Copyright (c) 2005-2007 Christophe Fillot. Build date: May 9 2009 18:06:28 ILT: loaded table \u0026#34;mips64j\u0026#34; from cache. ILT: loaded table \u0026#34;mips64e\u0026#34; from cache. ILT: loaded table \u0026#34;ppc32j\u0026#34; from cache. ILT: loaded table \u0026#34;ppc32e\u0026#34; from cache. A quick look via ifconfig shows all 12 interfaces up.\nLet’s test all of this with a simple topology. 3 routers to break out to a single 3560 switch:\nautostart = False [10.20.30.12:7200] workingdir = /data/dynamips/working [[3725]] image = /data/dynamips/ios/3725/c3725-adventerprisek9-mz.124-15.T14.UNCOMPRESSED.bin ram = 142 idlepc = 0x6026be14 ghostios = True [[ROUTER R5]] model = 3725 console = 2005 f0/0 = nio_linux_eth:eth4 f0/1 = nio_linux_eth:eth5 [[ROUTER R1]] model = 3725 console = 2001 f0/0 = nio_linux_eth:eth1 [[ROUTER R3]] model = 3725 console = 2003 f0/0 = nio_linux_eth:eth2 f0/1 = nio_linux_eth:eth3 I’ve started the topology up and connected the correct eth ports to the 3560. Let’s have a look at CDP:\nR3#sh cdp neighbors Capability Codes: R - Router, T - Trans Bridge, B - Source Route Bridge S - Switch, H - Host, I - IGMP, r - Repeater Device ID Local Intrfce Holdtme Capability Platform Port ID 3560TOP Fas 0/1 158 S I WS-C3560- Fas 0/6 3560TOP Fas 0/0 129 S I WS-C3560- Fas 0/4 What about on the switch itself?\n[email protected]:~$ telnet localhost 3000 3560TOP#sh cdp neighbors Capability Codes: R - Router, T - Trans Bridge, B - Source Route Bridge S - Switch, H - Host, I - IGMP, r - Repeater, P - Phone, D - Remote, C - CVTA, M - Two-port Mac Relay Device ID Local Intrfce Holdtme Capability Platform Port ID R3 Fas 0/6 152 R S I 3725 Fas 0/1 R3 Fas 0/4 151 R S I 3725 Fas 0/0 R1 Fas 0/5 150 R S I 3725 Fas 0/0 R5 Fas 0/7 149 R S I 3725 Fas 0/0 Finally, let’s do a layer 3 test between 2 routers going through the 3560:\nR5\ninterface FastEthernet0/0 ip address 10.1.1.5 255.255.255.0 R1\ninterface FastEthernet0/0 ip address 10.1.1.1 255.255.255.0 R1#ping 10.1.1.5 repeat 100 Type escape sequence to abort. Sending 100, 100-byte ICMP Echos to 10.1.1.5, timeout is 2 seconds: !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Success rate is 100 percent (100/100), round-trip min/avg/max = 1/4/24 ms Perfect. Everything works just as expected :)\n","permalink":"https://mellowd.co.uk/post/building-my-topology/","summary":"\u003cp\u003eSo now it’s time to actually build my topology. There are a number of issues I’d like to get my head around. It helps to know what the planned set up is going to be.\u003c/p\u003e\n\u003cp\u003eI plan to use my laptop for my studies. From my laptop I’ll be connecting to my dynamips box. My dynamips box is connected to 4 switches. I need to be able to console into all 4 switches remotely, but I don’t want to buy a terminal server. I also want to be able to telnet into all the routers running on the dynamips box.\u003c/p\u003e","title":"Building my topology"},{"content":"I’ve been busy with work and also getting my lab together so I can finally start my lab studies.\nI just need a couple more pieces and configuration and then I’m good to go. I’ll be using my dynamips box for the 9 routers (3725s) and 4 switches ( 2 x 3550s \u0026amp; 2 x 3560s)\nI’ll be sure to have a blog entry detailing how I’ve connected and configured everything to fit together. The dynamips box will also be my terminal server to connect to the 4 switches.\n","permalink":"https://mellowd.co.uk/post/lab-almost-complete/","summary":"\u003cp\u003eI’ve been busy with work and also getting my lab together so I can finally start my lab studies.\u003c/p\u003e\n\u003cp\u003eI just need a couple more pieces and configuration and then I’m good to go. I’ll be using my dynamips box for the 9 routers (3725s) and 4 switches ( 2 x 3550s \u0026amp; 2 x 3560s)\u003c/p\u003e\n\u003cp\u003eI’ll be sure to have a blog entry detailing how I’ve connected and configured everything to fit together. The dynamips box will also be my terminal server to connect to the 4 switches.\u003c/p\u003e","title":"Lab almost complete"},{"content":"It can be quite confusing to work out what all these terms are exactly referring to. I’ll try and put a concise answer for all of them here with an example.\nRIB – Routing Information Base This is the route table. i.e. When you do a show ip route, the RIB is what you see\nAR1#sh ip route Codes: C - connected, S - static, R - RIP, M - mobile, B - BGP D - EIGRP, EX - EIGRP external, O - OSPF, IA - OSPF inter area N1 - OSPF NSSA external type 1, N2 - OSPF NSSA external type 2 E1 - OSPF external type 1, E2 - OSPF external type 2 i - IS-IS, su - IS-IS summary, L1 - IS-IS level-1, L2 - IS-IS level-2 ia - IS-IS inter area, * - candidate default, U - per-user static route o - ODR, P - periodic downloaded static route Gateway of last resort is not set 10.0.0.0/32 is subnetted, 1 subnets C 10.255.255.5 is directly connected, Loopback0 A router may have many separate RIB’s. If you’re running vrf’s with customer, then each vrf will have a separate RIB:\nAR1#sh ip route vrf CUS1 | begin Gateway Gateway of last resort is not set C 192.168.1.0/24 is directly connected, FastEthernet0/0 AR1#sh ip route vrf CUS2 | begin Gateway Gateway of last resort is not set 172.16.0.0/24 is subnetted, 1 subnets C 172.16.1.0 is directly connected, FastEthernet2/0 FIB – Forwarding Information Base The FIB is an optimised version of the RIB. Or more correctly it’s the table a router looks at when deciding where to actually forward traffic. In Cisco land, the CEF table is a FIB.\nAR1#sh ip cef Prefix Next Hop Interface 0.0.0.0/0 drop Null0 (default route handler entry) 0.0.0.0/8 drop 0.0.0.0/32 receive 10.255.255.5/32 receive 127.0.0.0/8 drop 224.0.0.0/4 drop 224.0.0.0/24 receive 240.0.0.0/4 drop 255.255.255.255/32 receive Like the RIB, there can be multiple FIB tables on a router:\nAR1#sh ip cef vrf CUS1 Prefix Next Hop Interface 0.0.0.0/0 drop Null0 (default route handler entry) 0.0.0.0/32 receive 192.168.1.0/24 attached FastEthernet0/0 192.168.1.0/32 receive 192.168.1.1/32 receive 192.168.1.255/32 receive 224.0.0.0/24 receive 255.255.255.255/32 receive AR1#sh ip cef vrf CUS2 Prefix Next Hop Interface 0.0.0.0/0 drop Null0 (default route handler entry) 0.0.0.0/32 receive 172.16.1.0/24 attached FastEthernet2/0 172.16.1.0/32 receive 172.16.1.1/32 receive 172.16.1.255/32 receive 224.0.0.0/24 receive 255.255.255.255/32 receive LIB – Label Information Base The LIB is an MPLS table. This is the place where the router will keep all known MPLS labels. To take a look, you just need to use show mpls ldp bindings:\nAR1#sh mpls ldp bindings tib entry: 10.0.0.0/30, rev 18 local binding: tag: 21 remote binding: tsr: 10.255.255.1:0, tag: imp-null remote binding: tsr: 10.255.255.2:0, tag: imp-null tib entry: 10.0.0.4/30, rev 16 local binding: tag: 20 remote binding: tsr: 10.255.255.1:0, tag: 32 remote binding: tsr: 10.255.255.2:0, tag: imp-null tib entry: 10.0.0.8/30, rev 44 local binding: tag: 34 remote binding: tsr: 10.255.255.1:0, tag: 31 remote binding: tsr: 10.255.255.2:0, tag: 32 tib entry: 10.0.0.12/30, rev 42 local binding: tag: 33 remote binding: tsr: 10.255.255.1:0, tag: imp-null remote binding: tsr: 10.255.255.2:0, tag: 31 tib entry: 10.1.0.0/30, rev 5 local binding: tag: imp-null remote binding: tsr: 10.255.255.1:0, tag: imp-null remote binding: tsr: 10.255.255.2:0, tag: 30 tib entry: 10.2.0.0/30, rev 4 local binding: tag: imp-null remote binding: tsr: 10.255.255.1:0, tag: 30 remote binding: tsr: 10.255.255.2:0, tag: imp-null tib entry: 10.7.0.0/30, rev 36 local binding: tag: 30 remote binding: tsr: 10.255.255.1:0, tag: 25 remote binding: tsr: 10.255.255.2:0, tag: 26 tib entry: 10.8.0.0/30, rev 34 local binding: tag: 29 remote binding: tsr: 10.255.255.1:0, tag: imp-null remote binding: tsr: 10.255.255.2:0, tag: 25 tib entry: 10.255.255.1/32, rev 30 local binding: tag: 27 remote binding: tsr: 10.255.255.1:0, tag: imp-null remote binding: tsr: 10.255.255.2:0, tag: 23 tib entry: 10.255.255.2/32, rev 10 local binding: tag: 17 remote binding: tsr: 10.255.255.1:0, tag: 23 remote binding: tsr: 10.255.255.2:0, tag: imp-null tib entry: 10.255.255.3/32, rev 28 local binding: tag: 26 remote binding: tsr: 10.255.255.1:0, tag: 22 remote binding: tsr: 10.255.255.2:0, tag: 22 tib entry: 10.255.255.4/32, rev 26 local binding: tag: 25 remote binding: tsr: 10.255.255.1:0, tag: 21 remote binding: tsr: 10.255.255.2:0, tag: 21 tib entry: 10.255.255.5/32, rev 6 local binding: tag: imp-null remote binding: tsr: 10.255.255.1:0, tag: 20 remote binding: tsr: 10.255.255.2:0, tag: 20 tib entry: 10.255.255.7/32, rev 24 local binding: tag: 24 remote binding: tsr: 10.255.255.1:0, tag: 18 remote binding: tsr: 10.255.255.2:0, tag: 18 AR1# LFIB – Label Forwarding Instance Base The LFIB is another MPLS table. This is the table that the router uses to forward labelled packets going through the network. Much like the RIB uses the FIB to forward traffic, so the LIB uses the LFIB to forward traffic. This is how you view it:\nAR1#sh mpls forwarding-table Local Outgoing Prefix Bytes tag Outgoing Next Hop tag tag or VC or Tunnel Id switched interface 17 Pop tag 10.255.255.2/32 0 Se1/2 point2point 20 Pop tag 10.0.0.4/30 0 Se1/2 point2point 21 Pop tag 10.0.0.0/30 0 Se1/2 point2point Pop tag 10.0.0.0/30 0 Se1/0 point2point 24 18 10.255.255.7/32 0 Se1/0 point2point 25 21 10.255.255.4/32 0 Se1/2 point2point 26 22 10.255.255.3/32 0 Se1/0 point2point 27 Pop tag 10.255.255.1/32 0 Se1/0 point2point 29 Pop tag 10.8.0.0/30 0 Se1/0 point2point 30 25 10.7.0.0/30 0 Se1/0 point2point 33 Pop tag 10.0.0.12/30 0 Se1/0 point2point 34 32 10.0.0.8/30 0 Se1/2 point2point 31 10.0.0.8/30 0 Se1/0 point2point Originally publish with link https://mellowd.co.uk/ccie/?p=788\n","permalink":"https://mellowd.co.uk/post/rib-fib-lfib-lib/","summary":"\u003cp\u003eIt can be quite confusing to work out what all these terms are exactly referring to. I’ll try and put a concise answer for all of them here with an example.\u003c/p\u003e\n\u003ch2 id=\"rib--routing-information-base\"\u003eRIB – Routing Information Base\u003c/h2\u003e\n\u003cp\u003eThis is the route table. i.e. When you do a show ip route, the RIB is what you see\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eAR1#sh ip route\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eCodes: C - connected, S - static, R - RIP, M - mobile, B - BGP\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e       D - EIGRP, EX - EIGRP external, O - OSPF, IA - OSPF inter area\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e       N1 - OSPF NSSA external type 1, N2 - OSPF NSSA external type \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e       E1 - OSPF external type 1, E2 - OSPF external type \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e       i - IS-IS, su - IS-IS summary, L1 - IS-IS level-1, L2 - IS-IS level-2\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e       ia - IS-IS inter area, * - candidate default, U - per-user static route\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e       o - ODR, P - periodic downloaded static route\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eGateway of last resort is not set\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e     10.0.0.0/32 is subnetted, \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e subnets\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eC       10.255.255.5 is directly connected, Loopback0\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eA router may have many separate RIB’s. If you’re running vrf’s with customer, then each vrf will have a separate RIB:\u003c/p\u003e","title":"RIB, FIB, LFIB, LIB ETC"},{"content":"Steve over at networking-forum.com has a contest going, and the winner can win a few sweet prizes.\nTake a look here: http://www.networking-forum.com/viewtopic.php?f=29\u0026amp;t=23223\u0026amp;start=0\n","permalink":"https://mellowd.co.uk/post/creative-routing-contest/","summary":"\u003cp\u003eSteve over at \u003ca href=\"networking-forum.com\"\u003enetworking-forum.com\u003c/a\u003e has a contest going, and the winner can win a few sweet prizes.\u003c/p\u003e\n\u003cp\u003eTake a look here: \u003ca href=\"http://www.networking-forum.com/viewtopic.php?f=29\u0026amp;t=23223\u0026amp;start=0\"\u003ehttp://www.networking-forum.com/viewtopic.php?f=29\u0026amp;t=23223\u0026amp;start=0\u003c/a\u003e\u003c/p\u003e","title":"Creative Routing Contest"},{"content":"Wrote this morning. Just received my Pearson VUE confirmation.\nI now have my ticket to the LAB :D\nNow the real study starts!\nWOOHOO!!!\n","permalink":"https://mellowd.co.uk/post/350-001-ccie-written-v4-passed/","summary":"\u003cp\u003eWrote this morning. Just received my Pearson VUE confirmation.\u003c/p\u003e\n\u003cp\u003eI now have my ticket to the LAB :D\u003c/p\u003e\n\u003cp\u003eNow the real study starts!\u003c/p\u003e\n\u003cp\u003eWOOHOO!!!\u003c/p\u003e","title":"350-001 CCIE Written v4 passed"},{"content":"My last post about Traceroute got some interesting conversation going on in the comments.\nBasically there is quite a big difference in the way in which Windows and Linux handle traceroute. I tested on both Windows 7 and Ubuntu 10.04, but my guess is that all Windows follow the same format as do all *nix’s (please let me know if otherwise though!)\nI would recommend reading the above post again quickly to get all the basics out the way before we delve into the differences.\nStep-wise, this is what happens on Windows:\nThe OS send a DNS PTR request to 1.2.2.4.in-addr.arpa to get the hostname for 4.2.2.1 I get a DNS PTR response giving me a hostname The OS send an ICMP ECHO request with a TTL of 1 I get an ICMP TTL Exceeded packet back from my local router 3 \u0026amp; 4 above happens twice more The OS send a DNS PTR request to my local router My local router responds with it’s hostname The cycle above (3-7) is then repeated with a TTL of 2, then 3 and so on We finally get to 4.2.2.1 – which sends back an ICMP ECHO reply – Once I get 3 the job is complete. Ubuntu does this completely differently though. Step-wise this is what’s going on:\nThe OS immidiately sent 3 UDP packets with a high port number straight to 4.2.2.1 with a TTL of 1 The local router responded with 3X ICMP TTL Exceeded message The above (1 \u0026amp; 2) is then repeated until we get to 4.2.2.1 4.2.2.1 does not generate a ICMP ECHO reply as an ECHO request was not sent. Rather we get 3 ICMP Code 3 (Port unreachable) replies The OS now throws out 7 DNS PTR request specifically to each IP it determined in the path from above (Including 4.2.2.1 iself!) As soon as all the replies come, the job is complete. The main differences are that Windows will send a DNS PTR request from the start, then send ICMP ECHO requests. At each hop it’ll send a DNS PTR request and then move onto the next hop. Linux starts with sending UDP packets to a high port number straight away. When it finally gets to the last hop it’ll then send out a mass DNS PTR request to every hop in the path that it has determined.\n","permalink":"https://mellowd.co.uk/post/protocol-fundamentals-windows-vs-linux/","summary":"\u003cp\u003e\u003ca href=\"https://mellowd.co.uk/post/protocol-fundamentals-traceroute/\"\u003eMy last post about Traceroute\u003c/a\u003e got some interesting conversation going on in the comments.\u003c/p\u003e\n\u003cp\u003eBasically there is quite a big difference in the way in which Windows and Linux handle traceroute. I tested on both Windows 7 and Ubuntu 10.04, but my guess is that all Windows follow the same format as do all *nix’s (please let me know if otherwise though!)\u003c/p\u003e\n\u003cp\u003eI would recommend reading the above post again quickly to get all the basics out the way before we delve into the differences.\u003c/p\u003e","title":"Protocol Fundamentals – Traceroute differences between Windows and Linux"},{"content":"Traceroute is a powerful tool. Extremely useful when checking the path of a packet through the network. But how does it ACTUALLY work? What is REALLY going on?\nLayer3 packets all have a TTL. A Time To Live. If a router receives a packet with a TTL of 1 (and the packet is addresses to a host not directly connected to this router) it will drop the packet. It will also then create an ICMP error packet and send it back to the original source of the packet to let it know that the address was unreachable this time.\nIf you ping another machine, the OS will generally create a TTL of 255 for sent packets, though it doesn’t HAVE to be 255.\nTraceroute will force an ICMP error message so it can get more information from each hop in the path.\nAs an example, let’s run a quick traceroute to 4.2.2.1 and see what it gives us:\nC:\\Users\\Darren\u0026gt;tracert 4.2.2.1 Tracing route to vnsc-pri.sys.gtei.net [4.2.2.1] over a maximum of 30 hops: 1 1 ms \u0026lt;1 ms \u0026lt;1 ms DD-WRT [10.50.80.1] 2 8 ms 9 ms 8 ms 10.3.280.1 3 8 ms 9 ms 26 ms walt-cam-1a-ge96.network.virginmedia.net [80.1.170.69] 4 18 ms 7 ms 8 ms popl-core-1a-ae2-0.network.virginmedia.net [195.182.175.229] 5 8 ms 6 ms 16 ms popl-bb-1a-as2-0.network.virginmedia.net [213.105.174.234] 6 31 ms 15 ms 15 ms 195.50.91.69 7 12 ms 13 ms 31 ms ae-11-51.car1.London1.Level3.net [4.69.139.66] 8 14 ms 16 ms 22 ms vnsc-pri.sys.gtei.net [4.2.2.1] Trace complete. I’ve loaded up Wireshark to tell me exactly what’s going on. The very first packet sent from my PC was sent with a destination IP of 4.2.2.1 – but with a TTL of only 1.\nWhen my router (10.50.80.1) received that packet, it noticed that the TTL was 1, but also that 4.2.2.1 was not directly connected to it. It responded to my PC with an ICMP code 11 – Time-to-live exceeded. It also responded with it’s own source address.\nTraceroute now knows that the very first hop to 4.2.2.1 happens to be my local router. It also knows that the routers IP is 10.50.80.1 – It send 3 ECHO reply requests with a TTL of 1. This is the reason you see the 3 values in the traceroute output\nIt doesn’t stop there though. As soon as traceroute knows that 10.50.80.1 is the first hop, it’ll ask 10.50.80.1 what it’s hostname is via a DNS PTR request. The router will respond to my PC with a DNS PTR response letting it know the hostname. Now traceroute knows what the IP address and hostname is for the first hop. Note that not ALL devices on the internet will respond with a PTR record and so you won’t ALWAYS get a hostname.\nTraceroute will now start again and send 3 more packets with a TTL of 2. Exactly the same will happen as above, but for the second hop in the path. This will continue to happen until we eventually get to the host, or we run into the maximum hop count.\nWhen we finally get ICMP echo replies from 4.2.2.1 – traceroute knows it’s job is complete.\nbtw, traceroute attempts to get PTR records from the devices in the path only when it gets a TTL time exceeded reply. The actual host you are trying to get to is different though. As soon as I ran traceroute 4.2.2.1 it immediately tried to get the PTR record of 4.2.2.1 first. Once it had that it started with all of the above.\n","permalink":"https://mellowd.co.uk/post/protocol-fundamentals-traceroute/","summary":"\u003cp\u003eTraceroute is a powerful tool. Extremely useful when checking the path of a packet through the network. But how does it ACTUALLY work? What is REALLY going on?\u003c/p\u003e\n\u003cp\u003eLayer3 packets all have a TTL. A Time To Live. If a router receives a packet with a TTL of 1 (and the packet is addresses to a host not directly connected to this router) it will drop the packet. It will also then create an ICMP error packet and send it back to the original source of the packet to let it know that the address was unreachable this time.\u003c/p\u003e","title":"Protocol Fundamentals – traceroute"},{"content":"What is ARP and how does it actually work? I’m surprised at the amount of people who don’t know exactly what it does and how important it is.\nTo illustrate, I’m going to use this extremely simple network:\nBoth of these systems are really just connected to a home router. Remember that these ports are really just switched ports. The only time they traverse a layer3 port is when they are sending traffic outside the local LAN.\nARP is the Address Resolution Protocol. Essentially all it does is resolve a logical IP address to a physical Hardware (MAC) address.\nIn the above diagram, if 10.20.30.108 wants to send traffic to 10.20.30.4, it will move down the IOS layers. It will eventually get down to layer2. The layer2 header needs to have both a source and a destination MAC address. 10.20.30.108 has the layer3 address already, but not layer2. This is where ARP comes into the picture.\n10.20.30.108 will send a broadcast out onto the lan asking that whoever holds 10.20.30.4 respond with its MAC address (In that broadcast it’ll let everyone know what the MAC address of 10.20.30.108 is – so they can reply). When 10.20.30.4 get’s that broadcast, it’ll respond with it’s OWN MAC address with a unicast.\nOnce 10.20.30.108 has received 10.20.30.4′s MAC address, it will add that mapping to it’s own local ARP cache. As long as that value is in the cache, it’ll know exactly how and where to send traffic bound for 10.20.30.4\nAs an example, I’ve run the above through wireshark to see exactly what is happening (Click the image to see the full request and response):\nThe first ARP packet was a broadcast to the local lan asking for the owner of the 10.20.30.4 address. It also asks to respond to 10.20.30.108 (this ARP request also contains 10.20.30.108′s own MAC address) – The second packet is a simple unicast back to 10.20.30.108 letting it know that 10.20.30.4′s MAC address is 00:11:32:06:0c:8a\nThis can be verified as follows:\nC:\\Windows\\system32\u0026gt;arp -a Interface: 10.20.30.108 --- 0xb Internet Address Physical Address Type 10.20.30.4 00-11-32-06-0c-8a dynamic ARP is one of the fundamental parts of TCP/IP – Make sure you know it :) ","permalink":"https://mellowd.co.uk/post/protocol-fundamentals-arp/","summary":"\u003cp\u003eWhat is ARP and how does it actually work? I’m surprised at the amount of people who don’t know exactly what it does and how important it is.\u003c/p\u003e\n\u003cp\u003eTo illustrate, I’m going to use this extremely simple network:\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"lan\" loading=\"lazy\" src=\"/images/lan.png\"\u003e\u003c/p\u003e\n\u003cp\u003eBoth of these systems are really just connected to a home router. Remember that these ports are really just switched ports. The only time they traverse a layer3 port is when they are sending traffic outside the local LAN.\u003c/p\u003e","title":"Protocol Fundamentals – ARP"},{"content":"The main purpose of this post is to show how prefix lists work and how to decipher them vs regular access lists. Access-lists do a great job on Cisco devices, not just for security but all kinds of route filtering, QoS, and so on.\nA prefix list is a bit different form an access-list, and it’s important to know the differences and when to use either.\nI’ve created the following simple topology to illustrate what I’m going to be doing. There are 2 routers, both running BGP. Router1 will have numerous loopbacks with IP addresses that will be advertised into the BGP process. On router2 I’ll use various access-lists and prefix-lists to see what kind of results I get. Remember though that prefix-lists can be used with other routing protocols and not just BGP.\nThis is the topology: This is the config on each:\nR1#sh run | begin bgp router bgp 100 no synchronization bgp log-neighbor-changes network 1.1.1.1 mask 255.255.255.255 neighbor 10.1.1.10 remote-as 200 no auto-summary R2#sh run | begin bgp router bgp 200 no synchronization bgp log-neighbor-changes neighbor 10.1.1.9 remote-as 100 no auto-summary I’ll put the following subnets on R1 and advertise them in BGP:\n192.168.1.1/24 192.168.2.1/24 192.168.3.1/25 192.168.3.129/25 192.168.4.1/25 192.168.4.129/26 192.168.4.193/26 #R1 interface Loopback0 ip address 1.1.1.1 255.255.255.255 ! interface Loopback1 ip address 192.168.1.1 255.255.255.0 ! interface Loopback2 ip address 192.168.2.1 255.255.255.0 ! interface Loopback3 ip address 192.168.3.1 255.255.255.128 ! interface Loopback4 ip address 192.168.3.129 255.255.255.128 ! interface Loopback5 ip address 192.168.4.1 255.255.255.128 ! interface Loopback7 ip address 192.168.4.129 255.255.255.192 ! interface Loopback8 ip address 192.168.4.193 255.255.255.192 This is R1’s BGP config now:\nR1#sh run | begin bgp router bgp 100 no synchronization bgp log-neighbor-changes network 1.1.1.1 mask 255.255.255.255 network 192.168.1.0 network 192.168.2.0 network 192.168.3.0 mask 255.255.255.128 network 192.168.3.128 mask 255.255.255.128 network 192.168.4.0 mask 255.255.255.128 network 192.168.4.128 mask 255.255.255.192 network 192.168.4.192 mask 255.255.255.192 neighbor 10.1.1.10 remote-as 200 no auto-summary On Router2, we can see the routes advertised:\nR2#sh ip bgp BGP table version is 10, local router ID is 2.2.2.2 Status codes: s suppressed, d damped, h history, * valid, \u0026gt; best, i - internal, r RIB-failure, S Stale Origin codes: i - IGP, e - EGP, ? - incomplete Network Next Hop Metric LocPrf Weight Path *\u0026gt; 1.1.1.1/32 10.1.1.9 0 0 100 i *\u0026gt; 192.168.1.0 10.1.1.9 0 0 100 i *\u0026gt; 192.168.2.0 10.1.1.9 0 0 100 i *\u0026gt; 192.168.3.0/25 10.1.1.9 0 0 100 i *\u0026gt; 192.168.3.128/25 10.1.1.9 0 0 100 i *\u0026gt; 192.168.4.0/25 10.1.1.9 0 0 100 i *\u0026gt; 192.168.4.128/26 10.1.1.9 0 0 100 i *\u0026gt; 192.168.4.192/26 10.1.1.9 0 0 100 i Let’s say I want to filter out the network 192.168.4.0/25. If I use an access-list I need to do it as follows. Create the access list:\nR2#conf t R2(config)#access-list 5 deny 192.168.4.0 0.0.0.127 R2(config)#access-list 5 permit any Add a rule to the BGP config:\nR2#sh run | begin bgp router bgp 200 no synchronization bgp log-neighbor-changes neighbor 10.1.1.9 remote-as 100 neighbor 10.1.1.9 distribute-list 5 in no auto-summary You can see that the 192.168.4.0/25 route has now been filtered out:\nR2#sh ip bgp Network Next Hop Metric LocPrf Weight Path *\u0026gt; 1.1.1.1/32 10.1.1.9 0 0 100 i *\u0026gt; 192.168.1.0 10.1.1.9 0 0 100 i *\u0026gt; 192.168.2.0 10.1.1.9 0 0 100 i *\u0026gt; 192.168.3.0/25 10.1.1.9 0 0 100 i *\u0026gt; 192.168.3.128/25 10.1.1.9 0 0 100 i *\u0026gt; 192.168.4.128/26 10.1.1.9 0 0 100 i *\u0026gt; 192.168.4.192/26 10.1.1.9 0 0 100 i Let’s say I wanted to filter out the 192.168.4.x/26’s as well. In order to do so I’d have to add another line for each network in my access-list. With a prefix-list it’s much easier to do this. Let’s remove the access-list and start again. NB: Prefix-lists, like access-lists, have a implicit DENY at the end. In an ACL you’ll place a permit any at the end. The prefix-list version of this is to permit 0.0.0.0/0 le 32\nFirst I’ll create the prefix-list:\nR2(config)#ip prefix-list exclude_4 seq 5 deny 192.168.4.0/24 ge 25 le 26 R2(config)#ip prefix-list exclude_4 seq 10 permit 0.0.0.0/0 le 32 Now I’ll apply it to the BGP process:\nrouter bgp 200 no synchronization bgp log-neighbor-changes neighbor 10.1.1.9 remote-as 100 neighbor 10.1.1.9 prefix-list exclude_4 in no auto-summary When checking the BGP table I see the following:\nR2#sh ip bgp Network Next Hop Metric LocPrf Weight Path *\u0026gt; 1.1.1.1/32 10.1.1.9 0 0 100 i *\u0026gt; 192.168.1.0 10.1.1.9 0 0 100 i *\u0026gt; 192.168.2.0 10.1.1.9 0 0 100 i *\u0026gt; 192.168.3.0/25 10.1.1.9 0 0 100 i *\u0026gt; 192.168.3.128/25 10.1.1.9 0 0 100 i You can see that all the 192.168.4.1/25 and /26s are gone thanks to the prefix-list.\nThe basics of the prefix list is as follows. If I write\nip prefix-list exclude_4 seq 5 deny 192.168.4.0/24 ge 25 le 26 The /24 tells the IOS to match only the first 24 bits. i.e. 192.168.4 – I then tell the IOS to match only those prefixes that have a subnet mask of /25 or /26. i.e. If I had another network advertised which was 192.168.4.200/27 it would NOT match as even though the 192.168.4 part matches, it has a subnet mask of /27\nLet’s say I wanted to now match 192.168.x.x/25 but I wanted to leave the /26’s in place. This would be easy with a prefix list as follows:\nR2(config)#ip prefix-list exclude_4 seq 5 deny 192.168.3.0/16 ge 25 le 25 R2(config)#ip prefix-list exclude_4 seq 10 permit 0.0.0.0/0 le 32 I’ve told IOS to only match on the first 16 bits, i.e. 192.168 – I then told IOS to only match those prefixes that have a subnet mask of /25. If I apply this to my BGP process I can see that it works as expected:\nR2#sh ip bgp Network Next Hop Metric LocPrf Weight Path *\u0026gt; 1.1.1.1/32 10.1.1.9 0 0 100 i *\u0026gt; 192.168.1.0 10.1.1.9 0 0 100 i *\u0026gt; 192.168.2.0 10.1.1.9 0 0 100 i *\u0026gt; 192.168.4.128/26 10.1.1.9 0 0 100 i *\u0026gt; 192.168.4.192/26 10.1.1.9 0 0 100 i Only the 3 /25’s have disappeared, everything else is still there.\nYou can also do all of this with extended access-lists, but it’s so much more work, why make life difficult? Once you understand the context of prefix-lists it becomes very easy.\nOriginally publish with link https://mellowd.co.uk/ccie/?p=447\n","permalink":"https://mellowd.co.uk/post/access-lists-vs-prefix-lists/","summary":"\u003cp\u003eThe main purpose of this post is to show how prefix lists work and how to decipher them vs regular access lists.  Access-lists do a great job on Cisco devices, not just for security but all kinds of route filtering,  QoS, and so on.\u003c/p\u003e\n\u003cp\u003eA prefix list is a bit different form an access-list, and it’s important to know the differences and when to use either.\u003c/p\u003e\n\u003cp\u003eI’ve created the following simple topology to illustrate what I’m going to be doing. There are 2 routers, both running BGP. Router1 will have numerous loopbacks with IP addresses that will be advertised into the BGP process. On router2 I’ll use various access-lists and prefix-lists to see what kind of results I get. Remember though that prefix-lists can be used with other routing protocols and not just BGP.\u003c/p\u003e","title":"Access-lists vs Prefix-lists"}]