tag:blogger.com,1999:blog-75329908774985122872024-03-18T20:23:40.318-07:00Sam Russell CentralA network engineer's story of routers, switches, flows and packetsSam Russellhttp://www.blogger.com/profile/03431137767320819102noreply@blogger.comBlogger60125tag:blogger.com,1999:blog-7532990877498512287.post-35726214687131763592020-11-27T03:44:00.001-08:002020-11-27T03:44:39.353-08:00How to profile and fix your slow home wifi<h2 style="text-align: left;"> <span>Slow wifi</span></h2><p>There's a lot of us working from home these days, and that means a lot of Zoom and Skype video calls, Netflix, gaming, and all sorts of other internet traffic filling up your tubes. If you live in the city then you likely have a lot of neighbours nearby, and that means a lot of extra noise from their wireless routers. But how bad is it? Can you make it better? How can you tell?</p><h2 style="text-align: left;">Speed tests</h2><div>This is a good first step. There's a simple rule in software and networking: if you can't test it, it doesn't exist. I went to speedtest.net and had a look, and here's what I found:</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhkBYbbLR-pG_S-JYYkKp9znh4Gm6U3xBLmUPsxj8tAWibh7rvLW48D6yyvhfFZs3gWOlP0SMfTxHzUkjYlmqA7JrV8UJYhieeOFREbDnbMgQJokwvVIBY4DXVtpe0eigCoseUvUtswGeQI/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="141" data-original-width="744" height="76" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhkBYbbLR-pG_S-JYYkKp9znh4Gm6U3xBLmUPsxj8tAWibh7rvLW48D6yyvhfFZs3gWOlP0SMfTxHzUkjYlmqA7JrV8UJYhieeOFREbDnbMgQJokwvVIBY4DXVtpe0eigCoseUvUtswGeQI/w400-h76/Screen+Shot+2020-11-26+at+4.47.49+PM.png" width="400" /></a></div><br />This is where I should point out that my internet is 100Mb/s down, 10Mb/s up. We're getting nowhere near this on wifi. Here's the specs of the current setup:</div><div><ul style="text-align: left;"><li>Frequency: 2.4GHz</li><li>Technology: 802.11n</li><li>Connection speed: varies, but between 50 and 100Mb/s, allegedly</li></ul></div><div>Now there are a few things that can affect your internet speeds, but one of the main culprits is packet loss. It depends where in the network you get it though. If you get packet loss on a physical link (like a cable), then those packets will just disappear and your computer will have to resend them. This affects you when you're downloading or uploading files over TCP, but when you're on a Zoom or a Skype call, or some other live content like gaming, the software will be able to tolerate a certain amount of loss so long as the latency (or ping) remains stable. The speed test gives us a hint that there's a problem, but we need to look a bit deeper to find out what exactly is going on under the covers.</div><h2 style="text-align: left;">We have to go deeper</h2><div>I've used a lot of tools for network profiling over the years. I've worked with the perfSONAR framework, the WAND framework, straight iperf tests, but the one that I've settled upon is smokeping. Smokeping was designed to track latency to different hosts, but I've found you can use it for a lot more than that. Here are some of the extra things you can measure:</div><div><ul style="text-align: left;"><li>Packet loss: we need to tweak the defaults, but we can get good measurements on this</li><li>Jitter: this is a measure of how latency changes with time, we can definitely get a picture of this</li><li>Congestion: a little harder, but we can infer this from the graphs too</li><li>Bad links: this is a really fun one to figure out - is the fault in your local network, the other network, or somewhere in the middle? By graphing to a range of destinations along the path we can see which parts are clear and which parts are dirty and that'll point us to where the problems are being caused</li></ul><div>Let's get it set up. Luckily for us it's not the 90's anymore and we can just spin it up in a docker container. The team at linuxserver.io have kindly made a docker image for it, and their default docker-compose script is very good to get started with. All the details are at https://hub.docker.com/r/linuxserver/smokeping, but I'll put my script here for you as an example:</div></div><div><br /></div><div><div><span style="font-family: courier;">---</span></div><div><span style="font-family: courier;">version: "3.7"</span></div><div><span style="font-family: courier;">services:</span></div><div><span style="font-family: courier;"> smokeping:</span></div><div><span style="font-family: courier;"> image: linuxserver/smokeping</span></div><div><span style="font-family: courier;"> environment:</span></div><div><span style="font-family: courier;"> - PUID=1000</span></div><div><span style="font-family: courier;"> - PGID=1000</span></div><div><span style="font-family: courier;"> - TZ=Europe/London</span></div><div><span style="font-family: courier;"> volumes:</span></div><div><span style="font-family: courier;"> - ./config:/config</span></div><div><span style="font-family: courier;"> - ./config:/data</span></div><div><span style="font-family: courier;"> ports:</span></div><div><span style="font-family: courier;"> - 8001:80</span></div><div><span style="font-family: courier;"> restart: unless-stopped</span></div></div><div><br /></div><div>There we go. What this config does is the following:</div><div><br /></div><div><ul style="text-align: left;"><li>Runs the container with the timezone of your choosing</li><li>Mounts the folders "config" and "data" into the appropriate places in the container where smokeping is expecting to find them</li><li>Forwards port 8001 so we can go to the server in our browser.</li></ul><div>I normally put this in a folder called "smokeping" and create the two empty "config" and "data" folders inside. I then run the container as follows:</div></div><div><br /></div><div><span style="font-family: courier;">docker-compose up</span></div><div><br /></div><div>This will start the server, and you can navigate to it at http://localhost:8001 . You'll see a bunch of tabs on the left, and a bunch of graphs in the middle. This is all the default config; don't worry about this too much. You have smokeping up and running though!</div><h2 style="text-align: left;">Fine tuning</h2><div>The next step is to make smokeping do what we want. Stop smokeping by going back to your command prompt and typing Control+C and you'll see it start to shut down. Then we'll look at the config. Open the config folder, and you'll see a bunch of new files. The two files we care about are Probes and Targets. Open Probes and change the config to look like this:</div><div><br /></div><div><span style="font-family: courier;">*** Probes ***</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">+ FPing</span></div><div><span style="font-family: courier;">binary = /usr/sbin/fping</span></div><div><span style="font-family: courier;">pings = 5</span></div><div><span style="font-family: courier;">step = 30</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">+ DNS</span></div><div><span style="font-family: courier;">binary = /usr/bin/dig</span></div><div><span style="font-family: courier;">lookup = google.com</span></div><div><span style="font-family: courier;">pings = 5</span></div><div><span style="font-family: courier;">step = 30</span></div><div><br /></div><div>The default ping is too low and won't catch small changes, this update will increase it to 5 pings per 30 seconds (one ping every 6 seconds). You can increase the pings for FPing to 30 if you want, to do one ping per second (in 30 second chunks), and that will give us an even better look at packet loss over short periods of time. You could also do 5 pings per 5 seconds (ping = 5, step = 5), and this will do the same thing but group them in 5 second chunks on the graph. One warning here: every time you change this config, you'll have to delete your data (everything in the data directory) as the database format is tuned to these values.</div><div><br /></div><div>The next thing to do is pick some good targets. Open the Targets file, and delete all the targets except maybe Youtube and Facebook. Your config will look something like this now:</div><div><br /></div><div><span style="font-family: courier;">probe = FPing</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">menu = Top</span></div><div><span style="font-family: courier;">title = My smokeping</span></div><div><span style="font-family: courier;">remark = Remarkable</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">+ Home</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">menu = Home</span></div><div><span style="font-family: courier;">title = Home</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">++ Facebook</span></div><div><span style="font-family: courier;">menu = Facebook</span></div><div><span style="font-family: courier;">title = Facebook</span></div><div><span style="font-family: courier;">host = facebook.com</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">++ Youtube</span></div><div><span style="font-family: courier;">menu = YouTube</span></div><div><span style="font-family: courier;">title = YouTube</span></div><div><span style="font-family: courier;">host = youtube.com</span></div><div><br /></div><div>Now close these, delete the contents of your data directory, and restart smokeping with docker-compose up. You'll see the number of graphs has gone down to 2, and we can start working with these.</div><h2 style="text-align: left;">Number 3 will shock you</h2><div>Here's what my graphs looked like:</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEihMJrjaOlg5nXLugGBWrUh237wzoQFDK03ZDVKUlFiIYcK9f4DSUqdstBBs6o9I7Rtr2dPfsj6vtn7TNi6AxI0i_xIN330hYslpkBJr1r9QBGdeHvGT4fsPe0RLVB_ZNo-okHqj5Cc-YJy/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="310" data-original-width="697" height="178" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEihMJrjaOlg5nXLugGBWrUh237wzoQFDK03ZDVKUlFiIYcK9f4DSUqdstBBs6o9I7Rtr2dPfsj6vtn7TNi6AxI0i_xIN330hYslpkBJr1r9QBGdeHvGT4fsPe0RLVB_ZNo-okHqj5Cc-YJy/w400-h178/Facebook_last_10800.png" width="400" /></a></div><br />Looks like a bunch of fuzzy junk. Here's what's important about this graph:</div><div><ul style="text-align: left;"><li>All the dots are lime green. This is good; it means we're getting 0% packet loss. I thought my slowdowns might have been packet loss in my ISP's network, so it's nice to see this isn't a problem</li><li>There's a constant large range in latency. Lots of jumps from 20 to 160ms</li></ul><div>You can see to the right of the graph (after 18:20) the green line drops a little and all the noise goes away; this is when I plugged in an ethernet cable and used the internet through that. This tells me that all of the latency was caused by the wifi link. This is caused by packet retries on the wireless link, and this causes a ton of problems for us in practice. In short, packets that arrive out of order look a lot like lost packets and that gives you dropped connections, slow data rates, and laggy and unreliable Zoom and Skype calls.</div></div><div><br /></div><div>Why does this happen? The fact is, wifi will never be as reliable as a cable. The gap between your wireless card and your router is full of walls, people, and other household objects, and these all change the way the signal behaves. This is fine if you live in the middle of nowhere, but when you have a lot of neighbours nearby, their wifi signal will leak through your walls and means your internet becomes like a conversation in a noisy bar: slow, and full of shouting and misunderstandings. Wifi tries to solve this by having different wireless channels, but here are the problems with this:</div><div><ul style="text-align: left;"><li>2.4GHz wifi has 11 channels; this isn't enough when you have 30 networks nearby</li><li>The channels aren't perfectly separate, if channel 3 is strong then you'll still "hear" it on channel 1</li><li>Most APs will pick either channel 1, 6 or 11 to get around this, but that means you still have 10 other APs on whatever channel you pick</li></ul></div><h2 style="text-align: left;">Change the channel</h2><div>I ended up forking out for a new router that had 5GHz wifi. Apart from the fact that a lot of home wifi is still stuck on 2.4GHz (meaning less neighbours to compete with), the 5GHz frequency is also stopped by building materials (read: doesn't go through walls), so you can expect it to be "quieter", even if the neighbours all switch to 5GHz tomorrow, I can expect to have a stronger signal. So what are the new speeds on 5GHz?</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiX-hlUZTnpUpedKLzd3DqaUf9JGqhyphenhyphenoiO4LRQFKC0I61tORGuBduBXAly4uL8YWZRUuy_mQRqV4tdyras4KpEZbjZJm_XLUj34tmGdQZ5FRBVbpd3jIOLt9BCnA4c_0eJrGT3zFfOr362A/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="135" data-original-width="744" height="73" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiX-hlUZTnpUpedKLzd3DqaUf9JGqhyphenhyphenoiO4LRQFKC0I61tORGuBduBXAly4uL8YWZRUuy_mQRqV4tdyras4KpEZbjZJm_XLUj34tmGdQZ5FRBVbpd3jIOLt9BCnA4c_0eJrGT3zFfOr362A/w400-h73/Screen+Shot+2020-11-26+at+4.52.00+PM.png" width="400" /></a></div><br />That looks more like it. Here's the change in the packet latency and loss graphs:</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgUBN-7IFAaviC5MlYcS-PJKAfQKjEenUuYA8h8ibltyMk4fd0VnfJjvKSfRdHbtBk95mDktJC8Mt_B9iEQUAjy1Ekp6NmthOqB3VKG3i6GVnSf0MJjtMt0CFIq7drMWYmLURr1b-IXsSKj/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="310" data-original-width="697" height="178" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgUBN-7IFAaviC5MlYcS-PJKAfQKjEenUuYA8h8ibltyMk4fd0VnfJjvKSfRdHbtBk95mDktJC8Mt_B9iEQUAjy1Ekp6NmthOqB3VKG3i6GVnSf0MJjtMt0CFIq7drMWYmLURr1b-IXsSKj/w400-h178/Facebook_last_10800+%25281%2529.png" width="400" /></a></div><br />Note that while we still get the spikes in latency, they happen a lot less frequently. In practice this means just higher quality internet; a few days ago I'd get massive lag spikes while gaming online that would cut me off completely, whereas now I can have youtube streaming on one laptop, game on another, and have zero problems. I've had a couple Skype calls with great quality, and a Zoom call with my friends for Thanksgiving last night, and I definitely noticed the difference - before my audio would cut out and my video would be pixellated, but now everything is crisp and reliable with no lags and dropouts in the middle of the call.</div><div><br /></div><div>Finally, for completeness, I changed the ping rates for you from 5 to 15 per 30 seconds, and switched between 2.4GHz and 5GHz. I also added a new destination to my smokeping setup - my router is at 192.168.0.1, so I've used this config in my Targets file:<br /></div><div><br /></div><div><span style="font-family: courier;">++ Router</span></div><div><span style="font-family: courier;">menu = Router</span></div><div><span style="font-family: courier;">title = Router</span></div><div><span style="font-family: courier;">host = 192.168.0.1</span></div><div><br /></div><div>That lets me get graphs like this:</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0PWdCW14r-NlRdRFjOEkAkV6sA7AwgfAP4gTQF7KHO2Lv7aqkCavvKjFXqfrtY1y5YWjHwaVvgT3xysWGdc9gn6SMtujTYhDFCxw1oM1ZpC3ztk5viOdHbDgtNbyrZ9DmW-U2nO2H2Cga/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="310" data-original-width="697" height="178" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0PWdCW14r-NlRdRFjOEkAkV6sA7AwgfAP4gTQF7KHO2Lv7aqkCavvKjFXqfrtY1y5YWjHwaVvgT3xysWGdc9gn6SMtujTYhDFCxw1oM1ZpC3ztk5viOdHbDgtNbyrZ9DmW-U2nO2H2Cga/w400-h178/Router_last_10800+%25282%2529.png" width="400" /></a></div><br />The first part is on 5GHz, the middle bit is on 2.4GHz (with a machine reboot in the middle), and then back to 5GHz again. Note that at 15 pings per 30 seconds we see the green dots moving around a bunch on 2.4GHz and a lot more black fuzz, whereas on 5GHz it's a lot calmer; still not perfect like the cable, but noticably better. And to Facebook:</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhl0LWPNpZQoBvYmtYiqjPvUNJKIqdOfGU6EbfjhfXns2caJRxAB9MLbVBYJfUMNBcNCKQtrY68dlqHniLCwgZJl8nNt_kxV6JvQX344-rAjKS8jWFOrL7iJhPYZ1AfSrZtDzx0hIG0BqpA/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="310" data-original-width="697" height="178" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhl0LWPNpZQoBvYmtYiqjPvUNJKIqdOfGU6EbfjhfXns2caJRxAB9MLbVBYJfUMNBcNCKQtrY68dlqHniLCwgZJl8nNt_kxV6JvQX344-rAjKS8jWFOrL7iJhPYZ1AfSrZtDzx0hIG0BqpA/w400-h178/Facebook_last_10800+%25282%2529.png" width="400" /></a></div><br />The drop in the middle is because my 2.4GHz wifi router is using different DNS servers (on 5GHz I'm using a DNS geo unblocker so I get a slightly longer path to Youtube and Facebook). You can see the difference though, 5GHz on the left and right looks fairly clean, and 2.4GHz in the middle is messy and unreliable.</div><h2 style="text-align: left;">You need to measure it</h2><div>This is a nice story, but the important thing here isn't that moving to 5GHz magically fixed everything, or that my Skype and Zoom calls are all crystal clear or that my ping is perfect and I'm an absolute gamer pro now. The point is that you need to test and measure to find out what your problem is, and run the same tests again to see if you've fixed the problem or merely kicked the can down the road. I hope this gives you a chance to dip your toes in with docker and smokeping, and I hope this can help you diagnose (and even solve) network problems in your own home network. </div>Sam Russellhttp://www.blogger.com/profile/03431137767320819102noreply@blogger.com0tag:blogger.com,1999:blog-7532990877498512287.post-29827805533163287212017-11-15T13:31:00.001-08:002017-11-15T13:35:04.343-08:00How to get bitcoin cash out of Multibit HD<h2>
Get the keys</h2>
<div>
Multibit HD is no longer being actively developed, but you can extract your private keys to use in another wallet. KeepKey <a href="https://github.com/Multibit-Legacy/read-multibit-wallet-file" rel="nofollow" target="_blank">built a tool</a> to extract these keys, but it didn't work for me, so I built my own: <a href="https://github.com/samrussell/octobit" target="_blank">Octobit</a></div>
<h2>
Prerequisites</h2>
<div>
You need to have python installed, and the libraries in the requirements.txt file. If you're in Windows, get WSL and get python running there! Otherwise, you'll need to <a href="https://github.com/BurntSushi/nfldb/wiki/Python-&-pip-Windows-installation" rel="nofollow" target="_blank">install python and pip</a>, and then <a href="http://aka.ms/vcpython27" rel="nofollow" target="_blank">install MSVC for python</a>. Once you've done this, install the requirements and we're good to go</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiFryWjS_2i998uh1WfxkmJtqo6DEeJC6ca7xs9KPoHjnkEScq4Ap6aSOW9OaDFoolcwg49M4A9x1tCZLlTXX9ifPAICwVeMmQx80T1VWN84VUYaekV_-wD9I2MZeLl4kxDAul09rAn1KsK/s1600/install_pip.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="174" data-original-width="768" height="90" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiFryWjS_2i998uh1WfxkmJtqo6DEeJC6ca7xs9KPoHjnkEScq4Ap6aSOW9OaDFoolcwg49M4A9x1tCZLlTXX9ifPAICwVeMmQx80T1VWN84VUYaekV_-wD9I2MZeLl4kxDAul09rAn1KsK/s400/install_pip.PNG" width="400" /></a></div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhk44Tekc8sXfnaA7VeyFxAezDmXbr0ba6mXKzd5ptWj9W1pgCwbFhOEVDvbWwo7iqkJ_lyQSKKAS9gtis1V6Har5_K_MjMBhSrRJWJgEthbhlNNbd7yp65t2xPbMVzjJNLXNEYamwB2hxM/s1600/install_packages.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="373" data-original-width="1324" height="112" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhk44Tekc8sXfnaA7VeyFxAezDmXbr0ba6mXKzd5ptWj9W1pgCwbFhOEVDvbWwo7iqkJ_lyQSKKAS9gtis1V6Har5_K_MjMBhSrRJWJgEthbhlNNbd7yp65t2xPbMVzjJNLXNEYamwB2hxM/s400/install_packages.PNG" width="400" /></a></div>
<h2>
Using octobit</h2>
<div>
To export your keys, find your wallet file (~/Library/Application Support/MultiBitHD/<wallet-id>/mbhd.wallet.aes on OSX, or C:\Users\<username>\AppData\Roaming\MultiBitHD\<wallet-id>\mbhd.wallet.aes on Windows), and open it up on the commandline:</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLzwGBsaxJcCqptcCxpZnoJZqMHrJmL85JqlMFgXQMyPghC0YGi4yITEVpc2rq8UYmt-tRh56bN_j-y0E2wa3RHhHfF9O0ZXmvxgVNjsBSDqDVErHQjbx5cLykDpw2oKNJvKnQoZJHFGyo/s1600/run_octobit.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="149" data-original-width="1323" height="45" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLzwGBsaxJcCqptcCxpZnoJZqMHrJmL85JqlMFgXQMyPghC0YGi4yITEVpc2rq8UYmt-tRh56bN_j-y0E2wa3RHhHfF9O0ZXmvxgVNjsBSDqDVErHQjbx5cLykDpw2oKNJvKnQoZJHFGyo/s400/run_octobit.PNG" width="400" /></a></div>
<div>
<br /></div>
<div>
These are your wallet words - keep these secret! These allow anyone to spend your bitcoins - or your bitcoin cash.</div>
<h2>
Get the BCH</h2>
<div>
If you had bitcoins in this wallet before the Bitcoin Cash fork, then you'll have an equal amount of BCH sitting there. Let's load the wallet in <a href="https://electroncash.org/#download" rel="nofollow" target="_blank">ElectronCash</a> so that we can get access to them. I couldn't get the latest version of ElectronCash working on Windows, but I can confirm this all works under OSX - you need to choose to import a wallet, paste in the wallet words as a BIP39 seed, and then set m/0' as the path (watch the apostrophe) - and then watch your BCH turn up!</div>
Sam Russellhttp://www.blogger.com/profile/03431137767320819102noreply@blogger.com0tag:blogger.com,1999:blog-7532990877498512287.post-30110640778907439572016-12-30T18:47:00.000-08:002016-12-30T18:47:14.678-08:00Lernmi: Building a Neural Network in Ruby<h2>
Neural networks</h2>
<div>
A neural network is based somewhat on a real brain - the idea being that data comes into a bunch of neurons at the front, gets propagated through multiple layers, and an answer comes out the end. We've covered this a little in <a href="http://pieknywidok.blogspot.com/2016/12/reading-handwritten-numbers-with-neural.html" target="_blank">another blog post</a>, so here we'll just focus on what goes on under the covers.<br />
<h2>
Math time</h2>
</div>
<div>
A neural network is a type of non-linear function that can approximate any other function. How well it can approximate is based on the number of links it has between its layers of neurons. In other words, more neurons and more layers gives you a better (but slower) neural network. If you want to have a play with this idea, have a look at the <a href="http://playground.tensorflow.org/" target="_blank">Tensorflow Playground</a> - if you can't get it to match, try a mix of more layers and more neurons per layer.</div>
<h2>
More math time</h2>
<div>
We have a non-linear function, and we need to approximate something based on stochastic gradient descent. In other words, we get a bunch of samples as input, and we need to adjust the network based on what the output *should* be, and then the network will change to give us answers that are slightly closer to what we expect. If our training data is a diverse sample of our real data, then we can train a neural network on our training data, and it'll also work as well with other data that it hasn't seen before.<br />
<br />
This does seem kind of like magic though, right? What's going on under the covers?<br />
<h2>
The calculus bit</h2>
</div>
<div>
We generally use a method called Stochastic Gradient Descent, or a method related to it. The "stochastic" part means we randomly choose samples from our training data so that the model doesn't get biased, and all the little nudges we give it start to shape it in the way we want it to behave. The "gradient descent" part means for each sample, we look at how wrong the network currently is (the "error"), and calculate a gradient (or slope, if you like), and nudge the network in that direction.</div>
<div>
<br /></div>
<div>
This approach is robust and it works for a lot of things, but the hard part with a neural network is trying to find the gradient for every single weight in the network - if your network is 10 layers deep (a small network by modern standards), how does an error at the output adjust the weights 10 layers back at the start?</div>
<h2>
Backpropagation</h2>
<div>
We use partial derivatives to find the gradient at each layer, and then use that gradient to adjust the network. At each neuron, we take the sum of all of the downstream gradients (they get "backpropagated" to us), multiplied by the derivative of the activation function applied to the forward-propagated data. We then take this number, multiplied by the weight of the previous link, and add a fraction of this (multiplied by the training rate) to the current weight of that link. We also take this number multiplied by the forward-propagated data and backpropagate that to the previous neuron.</div>
<div>
<br /></div>
<div>
Simple... right?</div>
<div>
<br /></div>
<div>
This is some fiddly stuff if you're not currently a calculus student, and it took me a couple weeks to get my head around. You can find it all on the <a href="https://en.wikipedia.org/wiki/Backpropagation" target="_blank">wikipedia page for backpropagation</a>, and there are a couple of papers floating around the internet that explain it in different ways.</div>
<h2>
It shouldn't be this hard</h2>
<div>
The idea of multiplying partial derivatives is a simple one, and I spent a long time thinking about how to make this accessible. The architecture that I've settled on for Lernmi has made a couple of trade-offs, but I feel it breaks out the backpropagation into bite-sized pieces, and I hope that it makes it easier to understand.<br />
<h2>
Lernmi</h2>
</div>
<div>
I've published a neural network application in ruby, <a href="https://github.com/samrussell/lernmi" target="_blank">hosted here.</a> The logic is broken up between Neurons and Links, and instead of the word "gradient", I've used the word "sensitivity" as it better translates to what we're using it for - high sensitivity means we adjust the weight more, low sensitivity means we adjust it less.</div>
<div>
<br /></div>
<div>
When we propagate forwards, it's fairly simple. Each Link in a layer takes the output from it's input Neuron, multiplies it by its weight, and inputs it to its output Neuron. Each Neuron sums all of its inputs, applies the activation function (the sigmoid function), and waits for the next Link to take the resulting value.</div>
<div>
<br /></div>
<div>
When we backpropagate, the logic is spread across the Neurons and Links. For the output layer, the sensitivity is just the error (the actual output minus the expected output). Each Link in the last layer takes the sensitivity from its output Neuron, and multiplies this by the sensitivity of the activation function - we call this the "output sensitivity". The reason we do this is because our neurons use their activation function when they propagate, so we need to find the sensitivity of that, and we multiply it by the sensitivity of the output Neuron to get the sensitivity of the Link. Perhaps a better name could be the "link sensitivity"? We'll call it the "output sensitivity" for now though.</div>
<div>
<br /></div>
<div>
We use the output sensitivity in two ways: when we update the weight, we multiply it by its input value (the larger our input value, the more we probably contributed to the error), and adjust the weight of this Link by that amount. We also need to send a sensitivity back to the input Neuron - we multiply the output sensitivity by our weight, so that the earlier layer knows how much this Link contributed to the error.</div>
<h2>
Whose fault is this?</h2>
<div>
Don't be surprised if this is confusing the first time your read through. I would love to find a way to simplify this further - the principle is as simple as asking "what part of the network is to blame for the error". The math is a bit frustrating, as we're ultimately multiplying partial derivatives along the length of the network, but the goal of it is to do lots of tiny adjustments until the network is a good-enough approximation of the underlying data.</div>
<h2>
What are we approximating though?</h2>
<div>
This is where it gets a little freaky. It turns out that everything - handwriting, faces, pictures of various different objects, they all have a mathematical approximation. With a big enough neural network, you can recognise people, animals, objects, and various different styles of handwriting and signwriting. Even in a board game like chess or go, you can make a mathematical approximation of which moves are better, to the point where you can have a computer that can play at a world class level.</div>
<div>
<br /></div>
<div>
This isn't the same as intelligence; the computer isn't thinking, but at the same time, what is thinking anyway? Is what we call "thinking" and "judging" just a mathematical approximation in our heads?</div>
Sam Russellhttp://www.blogger.com/profile/03431137767320819102noreply@blogger.com0tag:blogger.com,1999:blog-7532990877498512287.post-48725433811561385662016-12-29T14:33:00.000-08:002016-12-29T14:33:25.350-08:00Reading handwritten numbers with a neural network<h2>
<span style="font-family: inherit;">
Computer vision with neural nets</span></h2>
<div>
<span style="font-family: inherit;">We're going to do a quick dive into how to get started with neural networks that can read text and recognise things in images. There are already a ton of techniques for computer vision that rely on a bunch of clever math, but there's something alluring about a platform as easy as a neural network.</span></div>
<h2>
<span style="font-family: inherit;">
Neural networks</span></h2>
<div>
<span style="font-family: inherit;">The idea is super super simple. You have a layer of input neurons, and they send their values along to the next layer, and this continues until you reach the end.</span></div>
<div>
<span style="font-family: inherit;"><br /></span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLccp-WwNwA0N2LvyCFd3Samyk1gHUjMzhpJiV7NjhuHhscxfNPpWEL7gLJn7CWocGR-ZFgQypGNz6UAi0uR0RdjnOHdjClI9Z5pxMMcUiRoWW99oLzR-SU3h_f3TazHnj0VmXyrcaU1p-/s1600/Colored_neural_network.svg.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><span style="font-family: inherit;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLccp-WwNwA0N2LvyCFd3Samyk1gHUjMzhpJiV7NjhuHhscxfNPpWEL7gLJn7CWocGR-ZFgQypGNz6UAi0uR0RdjnOHdjClI9Z5pxMMcUiRoWW99oLzR-SU3h_f3TazHnj0VmXyrcaU1p-/s320/Colored_neural_network.svg.png" width="266" /></span></a></div>
<div class="separator" style="clear: both; text-align: center;">
<span style="font-family: inherit;">CC BY-SA 3.0 <a href="https://commons.wikimedia.org/wiki/User_talk:Glosser.ca" style="background: none rgb(248, 249, 250); color: #0b0080; font-size: 13.3px; line-height: 21.28px; text-decoration: none;" title="User talk:Glosser.ca">Glosser.ca</a></span></div>
<div class="separator" style="clear: both; text-align: left;">
<span style="font-family: inherit;"><br /></span></div>
<div class="separator" style="clear: both; text-align: left;">
<span style="font-family: inherit;">The secret sauce here is the "weights" between each layer - a weight of 1.0 means we pass through the value as is, a weight of -1.0 means we pass through the opposite. We also use an "activation function" which is a way to add a some complexity to the numbers - ultimately this is what allows us to make neural networks process data in a meaningful manner.</span></div>
<div class="separator" style="clear: both; text-align: left;">
<span style="font-family: inherit;"><br /></span></div>
<div class="separator" style="clear: both; text-align: left;">
<span style="font-family: inherit;">When we talk about "training" a neural network, what we do is pass through some input data, look at the output, and then "backpropagate" the error. In other words, we tell the network what it should have given us as output, and then it goes back and adjust all its weights a little bit as a result. We consider a network trained when it gives us the right answer most of the time. We consider a neural network "overtrained" when it returns the right answer for data it has been trained on, but still gives us the wrong answer for similar data that it hasn't seen before. This isn't something you need to worry about now, but it's a good thing to keep in mind if you're having trouble in the future.</span></div>
<h2>
<span style="font-family: inherit;">
The MNIST database</span></h2>
<div>
<span style="font-family: inherit;">This is the best place to start. The MNIST database is a set of 70,000 handwritten digits split into two sets - 60,000 for training on, and 10,000 for testing on. You get a high score by training on the training set and then guessing as many of the testing set as possible. This means that you can't just memorise all the numbers - you need to have a program that can actually read digits and recognise them.</span></div>
<div>
<span style="font-family: inherit;"><br /></span></div>
<div>
<span style="font-family: inherit;">You can get a copy of the MNIST database from http://yann.lecun.com/exdb/mnist/</span></div>
<h2>
<span style="font-family: inherit;">
Building a number reader</span></h2>
<div>
<span style="font-family: inherit;">We'll be using Keras - it's a python library that lets you build and use neural networks. There's already some sample code for training on MNIST, so let's just go through how that works:</span></div>
<div>
<span style="font-family: inherit;"><br /></span></div>
<div>
<span style="font-family: inherit;">For starters, download keras</span></div>
<div>
<span style="font-family: inherit;"><br /></span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">pip install keras</span></div>
<div>
<span style="font-family: inherit;"><br /></span></div>
<div>
<span style="font-family: inherit;">Then download this script from the keras repo: <a href="https://raw.githubusercontent.com/fchollet/keras/master/examples/mnist_mlp.py">https://raw.githubusercontent.com/fchollet/keras/master/examples/mnist_mlp.py</a></span></div>
<div>
<span style="font-family: inherit;"><br /></span></div>
<div>
<span style="font-family: inherit;">Then fire away</span></div>
<div>
<span style="font-family: inherit;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">python mnist_mlp.py</span></div>
<div>
<span style="font-family: inherit;"><br /></span></div>
<div>
<span style="font-family: inherit;">Leave it for a bit, and watch the output</span></div>
<div>
<span style="font-family: inherit;"><br /></span></div>
<div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">Train on 60000 samples, validate on 10000 samples</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">Epoch 1/20</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">60000/60000 [==============================] - 9s - loss: 0.2453 - acc: 0.9248 - val_loss: 0.1055 - val_acc: 0.9677</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">Epoch 2/20</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">60000/60000 [==============================] - 9s - loss: 0.1016 - acc: 0.9692 - val_loss: 0.0994 - val_acc: 0.9676</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">Epoch 3/20</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">60000/60000 [==============================] - 10s - loss: 0.0753 - acc: 0.9772 - val_loss: 0.0868 - val_acc: 0.9741</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">Epoch 4/20</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">60000/60000 [==============================] - 12s - loss: 0.0598 - acc: 0.9818 - val_loss: 0.0748 - val_acc: 0.9787</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">Epoch 5/20</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">60000/60000 [==============================] - 12s - loss: 0.0515 - acc: 0.9843 - val_loss: 0.0760 - val_acc: 0.9792</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">Epoch 6/20</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">60000/60000 [==============================] - 12s - loss: 0.0433 - acc: 0.9873 - val_loss: 0.0851 - val_acc: 0.9796</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">Epoch 7/20</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">60000/60000 [==============================] - 11s - loss: 0.0382 - acc: 0.9884 - val_loss: 0.0773 - val_acc: 0.9820</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">Epoch 8/20</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">60000/60000 [==============================] - 11s - loss: 0.0342 - acc: 0.9900 - val_loss: 0.0829 - val_acc: 0.9821</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">Epoch 9/20</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">60000/60000 [==============================] - 11s - loss: 0.0333 - acc: 0.9901 - val_loss: 0.0917 - val_acc: 0.9812</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">Epoch 10/20</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">60000/60000 [==============================] - 12s - loss: 0.0297 - acc: 0.9915 - val_loss: 0.0943 - val_acc: 0.9804</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">Epoch 11/20</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">60000/60000 [==============================] - 11s - loss: 0.0262 - acc: 0.9927 - val_loss: 0.0961 - val_acc: 0.9823</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">Epoch 12/20</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">60000/60000 [==============================] - 11s - loss: 0.0244 - acc: 0.9926 - val_loss: 0.0954 - val_acc: 0.9823</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">Epoch 13/20</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">60000/60000 [==============================] - 12s - loss: 0.0248 - acc: 0.9938 - val_loss: 0.0868 - val_acc: 0.9828</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">Epoch 14/20</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">60000/60000 [==============================] - 12s - loss: 0.0235 - acc: 0.9938 - val_loss: 0.1007 - val_acc: 0.9806</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">Epoch 15/20</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">60000/60000 [==============================] - 12s - loss: 0.0198 - acc: 0.9946 - val_loss: 0.0921 - val_acc: 0.9837</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">Epoch 16/20</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">60000/60000 [==============================] - 15s - loss: 0.0195 - acc: 0.9946 - val_loss: 0.0978 - val_acc: 0.9842</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">Epoch 17/20</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">60000/60000 [==============================] - 15s - loss: 0.0208 - acc: 0.9946 - val_loss: 0.1084 - val_acc: 0.9843</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">Epoch 18/20</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">60000/60000 [==============================] - 14s - loss: 0.0206 - acc: 0.9947 - val_loss: 0.1112 - val_acc: 0.9816</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">Epoch 19/20</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">60000/60000 [==============================] - 13s - loss: 0.0195 - acc: 0.9951 - val_loss: 0.0986 - val_acc: 0.9845</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">Epoch 20/20</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">60000/60000 [==============================] - 11s - loss: 0.0177 - acc: 0.9956 - val_loss: 0.1152 - val_acc: 0.9838</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">Test score: 0.115194263857</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">Test accuracy: 0.9838</span></div>
</div>
<div>
<span style="font-family: inherit;"><br /></span></div>
<div>
<span style="font-family: inherit;">What just happened here? What do all those numbers mean? Is that good or bad?</span></div>
<h2>
<span style="font-family: inherit;">
Building a neural network</span></h2>
<div>
<span style="font-family: inherit;">Let's have a look at the code. There's a bunch of imports and prep at the start, but the important stuff is buried right at the bottom</span><br />
<span style="font-family: inherit;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">model = Sequential()</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">model.add(Dense(512, input_shape=(784,)))</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">model.add(Activation('relu'))</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">model.add(Dropout(0.2))</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">model.add(Dense(512))</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">model.add(Activation('relu'))</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">model.add(Dropout(0.2))</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">model.add(Dense(10))</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">model.add(Activation('softmax'))</span><br />
<span style="font-family: inherit; white-space: pre-wrap;"><br /></span>
<span style="font-family: inherit; white-space: pre-wrap;">We start off by creating a keras.Sequential() object, and then we add layers onto it. The first layer is a Dense layer that takes in a 784-point vector - this is the same size as the handwritten numbers. Each number is a 28x28 pixel black and white image, and 28x28 = 784 pixels. We set this in the "input_shape" parameter, and the other parameter is the number 512 - that's how many neurons are in this layer.</span></div>
<div>
<div style="word-wrap: break-word;">
<span style="font-family: inherit; white-space: pre-wrap;"><br /></span><span style="font-family: inherit; white-space: pre-wrap;">A Dense layer is the bread and butter of neural nets - they connect every neuron in the layer before to every neuron in the layer after. You'll see there are 3 being used here - the first two have 512 neurons each, and the third one has 10 neurons - we'll find out why in a second.</span></div>
<div style="word-wrap: break-word;">
<span style="font-family: inherit; white-space: pre-wrap;"><br /></span><span style="font-family: inherit; white-space: pre-wrap;">After the first Dense layer we have an Activation layer. The activation function we use here is "relu" - a Rectified Linear Unit. All it does is pass through any positive numbers, and round up any negative numbers to 0. These are an important part of neural networks - otherwise we're just adding the same numbers to each other.</span></div>
<div style="word-wrap: break-word;">
<span style="font-family: inherit; white-space: pre-wrap;"><br /></span><span style="font-family: inherit; white-space: pre-wrap;">After the Activation layer we have a Dropout layer. This is to fix a problem called "overfitting" - when your neural network memorises the training data but doesn't actually learn to recognise. You can detect this when you see your training loss drops but your validation loss stays high.</span><br />
<span style="font-family: inherit; white-space: pre-wrap;"><br /></span>
<span style="font-family: inherit; white-space: pre-wrap;">So this is how we build our neural network, but where do the images come from? How do the images and labels fit into this?</span><br />
<h2>
<span style="font-family: inherit; white-space: pre-wrap;">Preparing your data</span></h2>
<div>
<span style="font-family: inherit; white-space: pre-wrap;">This is an important step. The input data is a set of images and corresponding labels, like follows:</span></div>
<div>
<span style="font-family: inherit; white-space: pre-wrap;"><br /></span></div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiw0I3tf1HIeFqsMGdaPAuYpsLYc7fEmMQQ7I84Jr-_4fc-0s4CTFKJVzl1yjF394cDC9I45dZjWj_nDDIbfrNrkd5LuN2EggqtCt0aoDuE5zbQ5C533qZGRI0QXnsoWlhobtZhLTWK0QYc/s1600/172.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiw0I3tf1HIeFqsMGdaPAuYpsLYc7fEmMQQ7I84Jr-_4fc-0s4CTFKJVzl1yjF394cDC9I45dZjWj_nDDIbfrNrkd5LuN2EggqtCt0aoDuE5zbQ5C533qZGRI0QXnsoWlhobtZhLTWK0QYc/s400/172.png" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">A sample number 2</td></tr>
</tbody></table>
<div>
<span style="white-space: pre-wrap;">With Keras we can just import the dataset, but you'd normally load your images with a library like PIL and then convert them into numpy arrays by hand. First, let's look at the code we're using here:</span></div>
<div>
<span style="white-space: pre-wrap;"><br /></span></div>
<div>
<span style="white-space: pre-wrap;"><span style="font-family: Courier New, Courier, monospace; font-size: x-small;">(X_train, y_train), (X_test, y_test) = mnist.load_data()
X_train = X_train.reshape(60000, 784)
X_test = X_test.reshape(10000, 784)
X_train = X_train.astype('float32')
X_test = X_test.astype('float32')
X_train /= 255
X_test /= 255</span></span></div>
<div>
<span style="white-space: pre-wrap;"><br /></span></div>
<div>
<span style="white-space: pre-wrap;">This gives us lists of X_train, y_train, X_test, and y_test. The _train lists have the images, and the _test lists have the labels. We use the numpy.reshape method to change the images from 2-dimensional 28x28 pixel images to 1-dimensional 784 pixel images, as we're just using a Dense layer next. We then convert from integers to float32, and scale from the 0-255 range to the 0.0-1.0 range as neural networks tend to work best with numbers in this range.</span></div>
<div>
<span style="white-space: pre-wrap;"><br /></span></div>
<div>
<span style="white-space: pre-wrap;">This is all we need to do here - the rest just works!</span></div>
<h2>
What's next?</h2>
<div>
This is a fairly basic intro to Keras and neural networks, and there's a lot more you can do from here. We lose a lot of information by flattening the image to a 1-dimensional array, so we can get some improvements by using Convolution2D layers to learn a bit more about the shape of the numbers. We can also try making the network a bit deeper by layering more Convolution2D layers, and look at different training techniques.</div>
<div>
<br /></div>
<div>
That's all for now though, so get out there and start training some robots!</div>
</div>
<pre style="white-space: pre-wrap; word-wrap: break-word;"></pre>
<pre style="white-space: pre-wrap; word-wrap: break-word;"></pre>
<pre style="white-space: pre-wrap; word-wrap: break-word;"></pre>
</div>
Sam Russellhttp://www.blogger.com/profile/03431137767320819102noreply@blogger.com0tag:blogger.com,1999:blog-7532990877498512287.post-2758842979014842162015-12-08T00:19:00.002-08:002015-12-08T10:00:24.965-08:00MPLS testbed on Ubuntu Linux with kernel 4.3<h2>
MPLS in the kernel</h2>
<div>
Linux 4.3 was released last month, and one of the long-awaited features was MPLS support in the kernel. There is still a the odd <a href="http://www.spinics.net/lists/netdev/msg355610.html">bug to iron out</a>, but you can get a working MPLS testbed with the current kernel source (plus a single patch to fix a showstopper).<br />
<h2>
Building the kernel</h2>
</div>
<div>
<ol>
<li>Download the source of kernel 4.3 from here: <a href="https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-4.3.tar.xz">https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-4.3.tar.xz</a></li>
<li>Unpack the tarball (tar -xf linux-4.3.tar.xz)</li>
<li>Enter the newly-created linux-4.3 directory, run make menuconfig, and enable lwtunnel support, mpls-iptunnel support, mpls-gso support, and mpls-router support.</li>
<li>Apply the patch from <a href="http://git.kernel.org/cgit/linux/kernel/git/davem/net.git/diff/?id=fe82b3300ec9c0dc4ba871f9a58b265aadf4e186">http://git.kernel.org/cgit/linux/kernel/git/davem/net.git/diff/?id=fe82b3300ec9c0dc4ba871f9a58b265aadf4e186</a> (this fixes a problem with sending MPLS packets)</li>
<li>Build the kernel: make -j `getconf _NPROCESSORS_ONLN`</li>
<li>Once this has finished, build the debian packages: make -j `getconf _NPROCESSORS_ONLN` deb-pkg LOCALVERSION=-mplsfix</li>
<li>This will create a bunch of .deb files in the parent directory - copy both linux-image-4.3.0-mplsfix_amd64.deb and linux-headers-4.3.0-mplsfix_amd64.deb to the machine you want to install your new kernel on</li>
<li>Install the kernel with dpkg -i [package name]</li>
<li>Reboot, select Advanced options for booting Ubuntu, and choose your new kernel</li>
<li>You are all ready to go!</li>
</ol>
<div>
edit: easier way with a docker container: <a href="https://github.com/samrussell/kernelbuilder">https://github.com/samrussell/kernelbuilder</a></div>
<h2>
Enabling MPLS</h2>
</div>
<div>
The MPLS modules aren't loaded by default, so you'll need to load them yourself:</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">modprobe mpls_router</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">modprobe mpls_gso</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">modprobe mpls_iptunnel</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">sysctl -w net.mpls.conf.enp0s9.input=1</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">sysctl -w net.mpls.conf.lo.input=1</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">sysctl -w net.mpls.platform_labels=1048575</span></div>
</div>
<div>
<br /></div>
<div>
You'll need to set net.mpls.conf.[interface-name].input=1 for any other interfaces that you plan to receive MPLS packets on, otherwise the MPLS route table won't accept your routes.</div>
<h2>
Applying MPLS routes</h2>
<div>
The latest release of iproute2 isn't quite ready, so we'll need to live life on the bleeding edge and build this from source too</div>
<div>
<br /></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">git clone git://git.kernel.org/pub/scm/linux/kernel/git/shemminger/iproute2.git</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">cd iproute2</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">./configure</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">make</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">sudo make install</span></div>
<div>
<br /></div>
<div>
Once this is done, we can see that iproute2 has a few more options available for us - try ip route help and see what is available.</div>
<div>
<br /></div>
<div>
Some route examples:</div>
<div>
<br /></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">Routing 10.10.10.10/32 to 192.168.1.2 with label 100: ip route add 10.10.10.10/32 encap mpls 100 via inet 192.168.1.2</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span></div>
<div>
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;">Label swapping 100 for 200 and sent to 192.168.2.2: ip -f mpls route</span><span style="font-family: "courier new" , "courier" , monospace;"> add 100 as 200 via inet 192.168.2.2</span></span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: x-small;"><br /></span></span></div>
<div>
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;">Decapsulating label 300 and delivering locally: </span><span style="font-family: "courier new" , "courier" , monospace;">ip -f mpls route add 300 dev lo</span></span></div>
<h2>
Testbed setup</h2>
<div>
We're going to make use of network namespaces here to set up a couple of hosts. The plan is as follows:</div>
<div>
<ul>
<li>Base machine: has veth0 (plugs into veth1) and veth2 (plugs into veth3)</li>
<li>Host1: Has veth1 (plugs into veth0)</li>
<li>Host2: Has veth3 (plugs into veth2)</li>
</ul>
<div>
We will use label 111 for traffic from host1 to host2, and label 112 for traffic from host2 to host1. We will use penultimate hop popping here (as opposed to label swapping), but feel free to play with this and get different results.</div>
</div>
<div>
<br /></div>
<div>
Setup (all executed as root):</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">ip link add veth0 type veth peer name veth1</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">ip link add veth2 type veth peer name veth3</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">sysctl -w net.mpls.conf.veth0.input=1</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">sysctl -w net.mpls.conf.veth2.input=1</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">ifconfig veth0 10.3.3.1/24 up</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">ifconfig veth2 10.4.4.1/24 up</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">ip netns add host1</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">ip netns add host2</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">ip link set veth1 netns host1</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">ip link set veth3 netns host2</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">ip netns exec host1 ifconfig lo 10.10.10.1/32 up</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">ip netns exec host1 ifconfig veth1 10.3.3.2/24 up</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">ip netns exec host2 ifconfig lo 10.10.10.2/32 up</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">ip netns exec host2 ifconfig veth3 10.4.4.2/24 up</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">ip netns exec host1 ip route add 10.10.10.2/32 encap mpls 112 via inet 10.3.3.1</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">ip netns exec host2 ip route add 10.10.10.1/32 encap mpls 111 via inet 10.4.4.1</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">ip -f mpls route add 111 via inet 10.3.3.2</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">ip -f mpls route add 112 via inet 10.4.4.2</span></div>
</div>
<div>
<br /></div>
<div>
Testing (executed as root due to netns):</div>
<div>
<br /></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">ip netns exec host2 ping 10.10.10.1 -I 10.10.10.2</span></div>
<div>
<br /></div>
<div>
Results:</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">tcpdump -envi veth0</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">tcpdump: listening on veth0, link-type EN10MB (Ethernet), capture size 262144 bytes</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">21:14:14.687380 9a:08:f4:cf:aa:9c > 12:c7:db:9d:a5:25, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 63, id 53781, offset 0, flags [DF], proto ICMP (1), length 84)</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> 10.10.10.2 > 10.10.10.1: ICMP echo request, id 1359, seq 1, length 64</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">21:14:14.687404 12:c7:db:9d:a5:25 > 9a:08:f4:cf:aa:9c, ethertype MPLS unicast (0x8847), length 102: MPLS (label 112, exp 0, [S], ttl 64)</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><span class="Apple-tab-span" style="white-space: pre;"> </span>(tos 0x0, ttl 64, id 19009, offset 0, flags [none], proto ICMP (1), length 84)</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> 10.10.10.1 > 10.10.10.2: ICMP echo reply, id 1359, seq 1, length 64</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">21:14:15.701789 9a:08:f4:cf:aa:9c > 12:c7:db:9d:a5:25, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 63, id 53845, offset 0, flags [DF], proto ICMP (1), length 84)</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> 10.10.10.2 > 10.10.10.1: ICMP echo request, id 1359, seq 2, length 64</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">21:14:15.701810 12:c7:db:9d:a5:25 > 9a:08:f4:cf:aa:9c, ethertype MPLS unicast (0x8847), length 102: MPLS (label 112, exp 0, [S], ttl 64)</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><span class="Apple-tab-span" style="white-space: pre;"> </span>(tos 0x0, ttl 64, id 19246, offset 0, flags [none], proto ICMP (1), length 84)</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> 10.10.10.1 > 10.10.10.2: ICMP echo reply, id 1359, seq 2, length 64</span></div>
</div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span></div>
<div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">tcpdump -envi veth2</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">tcpdump: listening on veth2, link-type EN10MB (Ethernet), capture size 262144 bytes</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">21:14:45.714220 8e:d5:9d:07:9a:5c > d6:8a:7c:5e:5b:0f, ethertype MPLS unicast (0x8847), length 102: MPLS (label 111, exp 0, [S], ttl 64)</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><span class="Apple-tab-span" style="white-space: pre;"> </span>(tos 0x0, ttl 64, id 55648, offset 0, flags [DF], proto ICMP (1), length 84)</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> 10.10.10.2 > 10.10.10.1: ICMP echo request, id 1363, seq 1, length 64</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">21:14:45.714251 d6:8a:7c:5e:5b:0f > 8e:d5:9d:07:9a:5c, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 63, id 22394, offset 0, flags [none], proto ICMP (1), length 84)</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> 10.10.10.1 > 10.10.10.2: ICMP echo reply, id 1363, seq 1, length 64</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">21:14:46.717538 8e:d5:9d:07:9a:5c > d6:8a:7c:5e:5b:0f, ethertype MPLS unicast (0x8847), length 102: MPLS (label 111, exp 0, [S], ttl 64)</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><span class="Apple-tab-span" style="white-space: pre;"> </span>(tos 0x0, ttl 64, id 55848, offset 0, flags [DF], proto ICMP (1), length 84)</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> 10.10.10.2 > 10.10.10.1: ICMP echo request, id 1363, seq 2, length 64</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">21:14:46.717570 d6:8a:7c:5e:5b:0f > 8e:d5:9d:07:9a:5c, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 63, id 22412, offset 0, flags [none], proto ICMP (1), length 84)</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> 10.10.10.1 > 10.10.10.2: ICMP echo reply, id 1363, seq 2, length 64</span></div>
</div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span></div>
<div>
It works!</div>
<h2>
Next steps</h2>
<div>
We have software routers such as Quagga and BIRD, and these speak some of the more traditional protocols such as OSPF and BGP. We now need LDP daemons, and other linux software to stand up l2vpn and l3vpn.</div>
<div>
<br /></div>
<div>
Thanks to the team on the netdev mailing list, they have been super responsive and helpful.</div>
Sam Russellhttp://www.blogger.com/profile/03431137767320819102noreply@blogger.com10tag:blogger.com,1999:blog-7532990877498512287.post-63528052190588305612015-07-09T16:53:00.001-07:002015-07-09T16:53:23.615-07:00Deepdream: What do all the layers do?I spent last night getting my computer prepped for some <a href="http://googleresearch.blogspot.co.nz/2015/07/deepdream-code-example-for-visualizing.html">deep dreaming</a>, and it left me thinking: What do all the different layers do? There's over a hundred to choose from, so why not iterate through them all and see what happens?<br />
<br />
I used this as my starting point: https://github.com/Dhar/image-dreamer<br />
<br />
My base picture is one I took from a plane out of Queenstown (<a href="https://twitter.com/samrussellnz/status/619264890985844737">munged version here</a>), resized to 400px wide, and run through the layers as follows:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjjsYXX-MtqvuiaIZp-WXPPZSv-Q6zoJtAZ2vVKFAOWMzZziaAVGBjWf6wh2Vl3hg-fxmpqvAayS51XixXjdOrFV_qwKquzZviAjgvBLK2_3EKtf3LFrN2aXbp5a4mxa5U1PvkB7g087758/s1600/plane1.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjjsYXX-MtqvuiaIZp-WXPPZSv-Q6zoJtAZ2vVKFAOWMzZziaAVGBjWf6wh2Vl3hg-fxmpqvAayS51XixXjdOrFV_qwKquzZviAjgvBLK2_3EKtf3LFrN2aXbp5a4mxa5U1PvkB7g087758/s320/plane1.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">conv1/7x7_s2</td></tr>
</tbody></table>
<br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhW4LbJwt6gI0oOnnbSNiXPjZ8T_5-xYrH_9XZoeO3mj3OQbFMo-syq93DT7vTgzBcqrYuSOnW2XmJ17_i1s-N2dMN3xpbienzZ_yTf6l7SB427fvTfMGbnVyZJAMuZv5TWK32osSqbEJX8/s1600/plane2.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhW4LbJwt6gI0oOnnbSNiXPjZ8T_5-xYrH_9XZoeO3mj3OQbFMo-syq93DT7vTgzBcqrYuSOnW2XmJ17_i1s-N2dMN3xpbienzZ_yTf6l7SB427fvTfMGbnVyZJAMuZv5TWK32osSqbEJX8/s320/plane2.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">pool1/3x3_s2</td></tr>
</tbody></table>
<br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjdF_PwgedfWrm0h5JC9AY_3mfWXQxc-xa6b1q4MEUtTFdWYf3AND0TutI4SdUQIeeenFYDZBqHQRyZsya9bCwnmrPwrtYqvDZduqMmynIa5rXFwb8vvLubPcM5yXFFazQWkmFyPRoR6iIZ/s1600/plane3.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjdF_PwgedfWrm0h5JC9AY_3mfWXQxc-xa6b1q4MEUtTFdWYf3AND0TutI4SdUQIeeenFYDZBqHQRyZsya9bCwnmrPwrtYqvDZduqMmynIa5rXFwb8vvLubPcM5yXFFazQWkmFyPRoR6iIZ/s320/plane3.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">pool1/norm1<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEibFFVab-1F5Iyrrmkzrn1nGv_OGSeDWzHH5eYzgKCkXh1OFWv36A12QjbEPQti7nVnVgeyeq-ltUaNZIbPxDNoK17e4vTfjoo58lPRAqFNrzEQgzUh4a2OL3eT4GuWKufvLCfR-lPCJ-qe/s1600/plane4.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEibFFVab-1F5Iyrrmkzrn1nGv_OGSeDWzHH5eYzgKCkXh1OFWv36A12QjbEPQti7nVnVgeyeq-ltUaNZIbPxDNoK17e4vTfjoo58lPRAqFNrzEQgzUh4a2OL3eT4GuWKufvLCfR-lPCJ-qe/s1600/plane4.jpg" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">conv2/3x3_reduce<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgq71qqp3-XDReZBV_ubPkdH0okNxyYKjWNM9miXQkSORYV115Nv7q1b2JcgdGx-dCG9WQyuSIyrb92jbqfegrkX3mZGFYjkLS_lgfu3jlQ0XcwF_U74E75mf5C5fyNwV6OeksLEb9izp98/s1600/plane5.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgq71qqp3-XDReZBV_ubPkdH0okNxyYKjWNM9miXQkSORYV115Nv7q1b2JcgdGx-dCG9WQyuSIyrb92jbqfegrkX3mZGFYjkLS_lgfu3jlQ0XcwF_U74E75mf5C5fyNwV6OeksLEb9izp98/s320/plane5.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">conv2/3x3<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtQ5vqUPmxm2HpFXqaWgO6u5V6sPlz4_2XosTOl-EQE6gVxvvymk5xfD9nwV39M5w9movAoZQw09o1vhjCPf9t6A_Sz0GRmVk7cXFljSz8VKbM6tI4ChyphenhyphenCaoD8FTBOprb9UIC3mfjRQ22t/s1600/plane6.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtQ5vqUPmxm2HpFXqaWgO6u5V6sPlz4_2XosTOl-EQE6gVxvvymk5xfD9nwV39M5w9movAoZQw09o1vhjCPf9t6A_Sz0GRmVk7cXFljSz8VKbM6tI4ChyphenhyphenCaoD8FTBOprb9UIC3mfjRQ22t/s320/plane6.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">conv2/norm2<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFmRo7Vp7pTw4_YW1T4bTaEXJJsyo9iQv6pW18KSM_l0Bo3Dems6Jqc8yggTv3Eb1_UmvfdwUMATghQ-B9OxYjhtFkpJJotJABcDzvcunohpsnnfShZqmquc_jQzHVPPQ8caFzoe1zfYFW/s1600/plane7.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFmRo7Vp7pTw4_YW1T4bTaEXJJsyo9iQv6pW18KSM_l0Bo3Dems6Jqc8yggTv3Eb1_UmvfdwUMATghQ-B9OxYjhtFkpJJotJABcDzvcunohpsnnfShZqmquc_jQzHVPPQ8caFzoe1zfYFW/s320/plane7.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">pool2/3x3_s2<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgUDgR5SMbiJJEfJjDxLlZrCsAnAsOufFnh3ggVUqS0Y8oNPG_xA2Gn08YVEjH1VFQVRNRaVxkKQ-Ycj7NAUnNNXi8tnGctF9lHPNqLEjFiKPV2LZq4LzEUgJNv0nZy7mdnXj9nwpa8blv8/s1600/plane12.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgUDgR5SMbiJJEfJjDxLlZrCsAnAsOufFnh3ggVUqS0Y8oNPG_xA2Gn08YVEjH1VFQVRNRaVxkKQ-Ycj7NAUnNNXi8tnGctF9lHPNqLEjFiKPV2LZq4LzEUgJNv0nZy7mdnXj9nwpa8blv8/s320/plane12.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_3a/1x1<br /></td></tr>
</tbody></table>
<br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhVy5mz1tqpHYbp2hp2uLl8tFwUwbJJy-VOo_02mqSeBlsUoXxmDXl6GvP7UXZp_VWGadiRe9qIAmZOFR_32GsywEXNFRXowjrg40T1DbTtM784g9nMucYCJAK5UN8ZB27iJK2KjMUH0Cox/s1600/plane13.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhVy5mz1tqpHYbp2hp2uLl8tFwUwbJJy-VOo_02mqSeBlsUoXxmDXl6GvP7UXZp_VWGadiRe9qIAmZOFR_32GsywEXNFRXowjrg40T1DbTtM784g9nMucYCJAK5UN8ZB27iJK2KjMUH0Cox/s320/plane13.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_3a/3x3_reduce<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4sei4Vglf200esRaXxm4Qs-446OFlHemCoLayEg7JHHUVTcEesMzBGHxT1gSvXCWNPFMMi__9YVrAHan-A7GCuHcFcFfKexcpiI7rceD9C7PLmXSu9GjD31XQmfBRav8QsPRC5T98VXBX/s1600/plane14.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4sei4Vglf200esRaXxm4Qs-446OFlHemCoLayEg7JHHUVTcEesMzBGHxT1gSvXCWNPFMMi__9YVrAHan-A7GCuHcFcFfKexcpiI7rceD9C7PLmXSu9GjD31XQmfBRav8QsPRC5T98VXBX/s320/plane14.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_3a/3x3<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtqSeQDD5LQXFXAg3IWpZjaLNAsm_pIQfhy6_GuBNgEXk_mIDvIMz0ndmK6jliIYJRgjdo7ur8YWx8Zy3v6KtD8eWK82SidsbDjtwFW0Lr179_8TNTzCzJ9JoCq_DR1OJSN1edgJCtiNZW/s1600/plane15.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtqSeQDD5LQXFXAg3IWpZjaLNAsm_pIQfhy6_GuBNgEXk_mIDvIMz0ndmK6jliIYJRgjdo7ur8YWx8Zy3v6KtD8eWK82SidsbDjtwFW0Lr179_8TNTzCzJ9JoCq_DR1OJSN1edgJCtiNZW/s320/plane15.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_3a/5x5_reduce<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjLKcHIiQcy0AwkiEtQDw_m-I-0QR4DyGCE9kVFJIS7QohycGKd5m-MrJrkUBRdk0fdMq6GUHD43Qjc4_2ieuW4HcnONoNicNpVQvu0N1xc-7BfYarxLYfNyQh6l8ej8XMdXjsJosIuxdpH/s1600/plane16.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjLKcHIiQcy0AwkiEtQDw_m-I-0QR4DyGCE9kVFJIS7QohycGKd5m-MrJrkUBRdk0fdMq6GUHD43Qjc4_2ieuW4HcnONoNicNpVQvu0N1xc-7BfYarxLYfNyQh6l8ej8XMdXjsJosIuxdpH/s320/plane16.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_3a/5x5<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjK17F4UYZ-bLROE7M5_tnZ_wRezf8L_2q_sXj8hcT9OBSxhjDHdDAQhoWe0hPCiMxslggU1S0h2ZBZw3G3_EdiDQ1AxkuAf8WzXKBs2d49X530C8G_TmtZuCJAIjq4qZseaJne87Tmc1wC/s1600/plane17.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjK17F4UYZ-bLROE7M5_tnZ_wRezf8L_2q_sXj8hcT9OBSxhjDHdDAQhoWe0hPCiMxslggU1S0h2ZBZw3G3_EdiDQ1AxkuAf8WzXKBs2d49X530C8G_TmtZuCJAIjq4qZseaJne87Tmc1wC/s320/plane17.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_3a/pool<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh6pNkJ77xvF_u4cZ1PxqyP_uTIWvz3iCrXtPR-LOF-de4AyRK__up6r4eUjFWxfphyq65vp8i8uXJrFRBTvG9Nf9g9rE0WBG4oQnKddl1_umqrtULaAC2VZbxbfUqB4jq2ATdlB5uiWUbY/s1600/plane18.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh6pNkJ77xvF_u4cZ1PxqyP_uTIWvz3iCrXtPR-LOF-de4AyRK__up6r4eUjFWxfphyq65vp8i8uXJrFRBTvG9Nf9g9rE0WBG4oQnKddl1_umqrtULaAC2VZbxbfUqB4jq2ATdlB5uiWUbY/s320/plane18.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_3a/pool_proj<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhfYRV2y72bNw2R9BimeVGo9T9eqRuKEL_xAVRgsq1cdlwDDuhytvhSgFbPnYsjkSgasZ8YuVyNUCWn88Cd_WVH4Ya0BbIs_FYzg9DK4XSzj5SMGpPNSSw2wsPiNPizSXdzAlYJEmzKVkrV/s1600/plane19.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhfYRV2y72bNw2R9BimeVGo9T9eqRuKEL_xAVRgsq1cdlwDDuhytvhSgFbPnYsjkSgasZ8YuVyNUCWn88Cd_WVH4Ya0BbIs_FYzg9DK4XSzj5SMGpPNSSw2wsPiNPizSXdzAlYJEmzKVkrV/s320/plane19.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_3a/output<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjyFfi2m1xKUpb-X5Z8zoQDlSPfPfCOcQY2xuJxtSIx2Cm_LXdSttPDRHs4Z8wST1nWOFcjVJGC7ymp0gI3AYG2BdgFkoiKvka-njQzXG1Gwd9jFnGT6unjYcaKXovKifPM8BDxt8sPAK8a/s1600/plane24.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjyFfi2m1xKUpb-X5Z8zoQDlSPfPfCOcQY2xuJxtSIx2Cm_LXdSttPDRHs4Z8wST1nWOFcjVJGC7ymp0gI3AYG2BdgFkoiKvka-njQzXG1Gwd9jFnGT6unjYcaKXovKifPM8BDxt8sPAK8a/s320/plane24.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_3b/1x1<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiMqA7QDstH3_1QQSUgF4XBFRfG7V9jl67gmrxEyx5mCReZCRLVvlZImXhn4wJ5urJdV1REN5E-YoY1YvEk9LS9WhS1TG5HrqQRhwQCLJcNIfbG2qk878YJ7jtTazZnfN8Ft8kTertsCK_H/s1600/plane25.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiMqA7QDstH3_1QQSUgF4XBFRfG7V9jl67gmrxEyx5mCReZCRLVvlZImXhn4wJ5urJdV1REN5E-YoY1YvEk9LS9WhS1TG5HrqQRhwQCLJcNIfbG2qk878YJ7jtTazZnfN8Ft8kTertsCK_H/s320/plane25.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_3b/3x3_reduce<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhRg_rbGHj1igcvvKrNLypp0xQvXXuBCpdlgaDvg_Aktuz-Dngh8l8RnN5rPHVGovyAAP7Al0K-HWJaYEr_O-hnu3QneiwwTztaxjeRQIfIikMqPRs0nZRwW5N06xraCQb2AD68c_-L81eI/s1600/plane26.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhRg_rbGHj1igcvvKrNLypp0xQvXXuBCpdlgaDvg_Aktuz-Dngh8l8RnN5rPHVGovyAAP7Al0K-HWJaYEr_O-hnu3QneiwwTztaxjeRQIfIikMqPRs0nZRwW5N06xraCQb2AD68c_-L81eI/s320/plane26.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_3b/3x3<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiUaVE8Ui9fxAnaQX2OAYW4E2a5x2rkUGhp2RofVUEg0z1OKo2ZXLoaYevNeL59W1A0AC9OsKInWbMTkF0tUMVMXGzq2IAsLYoQUwBtU4cxZL6bQnyB09QgUlM6y67kywVMbzBwXppcXIQq/s1600/plane27.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiUaVE8Ui9fxAnaQX2OAYW4E2a5x2rkUGhp2RofVUEg0z1OKo2ZXLoaYevNeL59W1A0AC9OsKInWbMTkF0tUMVMXGzq2IAsLYoQUwBtU4cxZL6bQnyB09QgUlM6y67kywVMbzBwXppcXIQq/s320/plane27.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_3b/5x5_reduce<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiCrFAh57MloGNS8XpZ22RByD7LF9jAzOtxwq0SD8vbqmxe2r3-5ovUipaKKRed3LLMjfyhQfex2os2SXs2lV5QxZwrF_pB2X9xY_Wq8FYbVj2iJr3Nb1AgPRUExFwD2D3_pds21fb_faN3/s1600/plane28.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiCrFAh57MloGNS8XpZ22RByD7LF9jAzOtxwq0SD8vbqmxe2r3-5ovUipaKKRed3LLMjfyhQfex2os2SXs2lV5QxZwrF_pB2X9xY_Wq8FYbVj2iJr3Nb1AgPRUExFwD2D3_pds21fb_faN3/s320/plane28.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_3b/5x5<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhyxFDo6fId2RC5EHlf90s9BcE8JH_61pCV1Al5MKQwBdTALxF9d5EsQuEmubkY2TAZ7vPL-K_AOsQ4wbuJ90TDch6ADbf87lykAPbc8cxrZhwfIQi7dhPg83gZWbiiIc4I-aFrtk-b8_Oo/s1600/plane29.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhyxFDo6fId2RC5EHlf90s9BcE8JH_61pCV1Al5MKQwBdTALxF9d5EsQuEmubkY2TAZ7vPL-K_AOsQ4wbuJ90TDch6ADbf87lykAPbc8cxrZhwfIQi7dhPg83gZWbiiIc4I-aFrtk-b8_Oo/s320/plane29.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_3b/pool<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiOH2a49OPoAxvsFpwrBF23YUOgUqfrkegfEdLLHug5yhRJiK4HQPK43Y51mpBWEvqhTRKzgyyAFmBPzP_EGYkyzRF8enu9H5oC-ZWkk5yfOMZ1vci1XUoflhfE5pR6LOrqVAZkq5FeqH6T/s1600/plane30.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiOH2a49OPoAxvsFpwrBF23YUOgUqfrkegfEdLLHug5yhRJiK4HQPK43Y51mpBWEvqhTRKzgyyAFmBPzP_EGYkyzRF8enu9H5oC-ZWkk5yfOMZ1vci1XUoflhfE5pR6LOrqVAZkq5FeqH6T/s320/plane30.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_3b/pool_proj<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTOHA4_nLVJnFx1_csBquEVnq0OUfOEszk2Eo5WoNewdRodOds6DO6Cy7dBoxN-dn0xjHgleul5nCGF0ucADuRqCdY6ypPDzA25ezIxHEv_bMGmDFcaB_21EFYnZVUz2ZimXZv5a4jwWSK/s1600/plane31.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTOHA4_nLVJnFx1_csBquEVnq0OUfOEszk2Eo5WoNewdRodOds6DO6Cy7dBoxN-dn0xjHgleul5nCGF0ucADuRqCdY6ypPDzA25ezIxHEv_bMGmDFcaB_21EFYnZVUz2ZimXZv5a4jwWSK/s320/plane31.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_3b/output<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjISj5wh1zztXvEeQWa9M2qW1-LfD_qH_XsImCXUCRmgaBmgqf2YmqLSoRiCNHy1pRsb0Jkz12ZzOrZBAj9wHCPjGBb1c9l4CxaVX5vSH7iTJXVTo9n9GSMgPGwml2G-_W5j1pF9-hM_HnF/s1600/plane32.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjISj5wh1zztXvEeQWa9M2qW1-LfD_qH_XsImCXUCRmgaBmgqf2YmqLSoRiCNHy1pRsb0Jkz12ZzOrZBAj9wHCPjGBb1c9l4CxaVX5vSH7iTJXVTo9n9GSMgPGwml2G-_W5j1pF9-hM_HnF/s320/plane32.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">pool3/3x3_s2<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHSUEC1sWW0QU6sntwWy6N3iSgOa00A2lOK8DhMdJpo4muRdm2VyVsvyox5MFkpA6HiuPIRwxm2IcVBFk4-pJCz22IxM-IGVea8miiq_NYacIrxiEv74NxNC0G4RpHyJhMtzHbWN_q5Byd/s1600/plane37.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHSUEC1sWW0QU6sntwWy6N3iSgOa00A2lOK8DhMdJpo4muRdm2VyVsvyox5MFkpA6HiuPIRwxm2IcVBFk4-pJCz22IxM-IGVea8miiq_NYacIrxiEv74NxNC0G4RpHyJhMtzHbWN_q5Byd/s320/plane37.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_4a/1x1<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgR2tR7yuyLt4-YXwQEOs5sOvfqoQqoeEz_ISENptCYbkSh7MN68vh-Kkrty8kL7UdFj1zj3Zj6m5wvL3-mmWOplirszxOKjsEO-QOLTTIIzYDNIm22OVOXuSnp42lYWE1CgZJPOOc5d2TO/s1600/plane38.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgR2tR7yuyLt4-YXwQEOs5sOvfqoQqoeEz_ISENptCYbkSh7MN68vh-Kkrty8kL7UdFj1zj3Zj6m5wvL3-mmWOplirszxOKjsEO-QOLTTIIzYDNIm22OVOXuSnp42lYWE1CgZJPOOc5d2TO/s320/plane38.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_4a/3x3_reduce<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEip6HilLPqoCEEPYMryahNshCyfeSZ_iBwWEo8MrMwATzrqbhasf8UYV3QBMvsL1_tKJOO4Atc__OdwAS-82ONj-C4XYZPKTxVq8Bx3h0lsKKFrtdDfU57KEOtyCjon-UbEwc6z_zB2xBMQ/s1600/plane39.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEip6HilLPqoCEEPYMryahNshCyfeSZ_iBwWEo8MrMwATzrqbhasf8UYV3QBMvsL1_tKJOO4Atc__OdwAS-82ONj-C4XYZPKTxVq8Bx3h0lsKKFrtdDfU57KEOtyCjon-UbEwc6z_zB2xBMQ/s320/plane39.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_4a/3x3<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhp_HhyphenhyphenXMUyJgaZpZ5Ujy7aZOo51VyIg35gIqte0x_vFnaA0w-auLiBKovg1Sq9YtY5eBnJHx3jIDvqif4NJteEO7y7L0EOZwlNLeS0XsLTl3kR-tP7LBZ80GkgboCsal3dfnrbpZ7KXf_x/s1600/plane40.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhp_HhyphenhyphenXMUyJgaZpZ5Ujy7aZOo51VyIg35gIqte0x_vFnaA0w-auLiBKovg1Sq9YtY5eBnJHx3jIDvqif4NJteEO7y7L0EOZwlNLeS0XsLTl3kR-tP7LBZ80GkgboCsal3dfnrbpZ7KXf_x/s320/plane40.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_4a/5x5_reduce<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhJUs9aW6HW5kfoj8O8ddcu9WQSlup3eHrMk10R93Yi6ru5HqWH0zU69RmOtBm_9fZIQ13J3Zfx_81QBwfuxvrBmllD87BTMKhiMDdqhSePa9PNLnRI6vsxMXg3WC-3y6r9Rie5c6ZOcCU4/s1600/plane41.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhJUs9aW6HW5kfoj8O8ddcu9WQSlup3eHrMk10R93Yi6ru5HqWH0zU69RmOtBm_9fZIQ13J3Zfx_81QBwfuxvrBmllD87BTMKhiMDdqhSePa9PNLnRI6vsxMXg3WC-3y6r9Rie5c6ZOcCU4/s320/plane41.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_4a/5x5<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEieaIzMaHfT6-Mzib5wC9EnKroDyR_9I3gtv_solK8zSOTu67TrS0yFsJSeN4gTFcUPxgihQXpmG3WPXxqSm_gz4ZlMHb1XmOqr5OBZnX88QLj9bvJYsQ8QWHcjtrnF3s2OBtoVs0sTDTH2/s1600/plane42.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEieaIzMaHfT6-Mzib5wC9EnKroDyR_9I3gtv_solK8zSOTu67TrS0yFsJSeN4gTFcUPxgihQXpmG3WPXxqSm_gz4ZlMHb1XmOqr5OBZnX88QLj9bvJYsQ8QWHcjtrnF3s2OBtoVs0sTDTH2/s320/plane42.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_4a/pool<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEggeH4X3MXZzEHtzGPvYvmLG_af5IhOvU_SOPZtmSx_eoSMaUKC5xTm0bclpihr3TDLiWPcQY3MBcs-1lqHGkrpCkHpx0VSTrJ45MnlSlD3SB9ECbpg555ETzP6oZNgBOy3tqZrtz79seUl/s1600/plane43.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEggeH4X3MXZzEHtzGPvYvmLG_af5IhOvU_SOPZtmSx_eoSMaUKC5xTm0bclpihr3TDLiWPcQY3MBcs-1lqHGkrpCkHpx0VSTrJ45MnlSlD3SB9ECbpg555ETzP6oZNgBOy3tqZrtz79seUl/s320/plane43.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_4a/pool_proj<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg0w7tIGDJf1orkGy_V9LMlnC6LPmSDZ_OF_Tfb4H1p_SJFfMQrv9NlZM2GCXA-TXVwBZkVr0y6VpAB6smKaDN6EJ5L0-WItzWleZ3q1o2opaB9i0AUemQyqCmoYY6pR-4YoXCETWfQRwKm/s1600/plane44.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg0w7tIGDJf1orkGy_V9LMlnC6LPmSDZ_OF_Tfb4H1p_SJFfMQrv9NlZM2GCXA-TXVwBZkVr0y6VpAB6smKaDN6EJ5L0-WItzWleZ3q1o2opaB9i0AUemQyqCmoYY6pR-4YoXCETWfQRwKm/s320/plane44.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_4a/output<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_uWqWNrEI9xfSX5zu9d1r-vKnnYr_DIjxO1WfaL9tpY9QPe0M-qAH0AoR-SAvK9VT_wZrac2YwaHnN_DvWcttpCZAhMQ3tKQjogtcfBXeOtnMnYTXLafahgZuTnBzQfm_JgFGhZ1L9YCC/s1600/plane49.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_uWqWNrEI9xfSX5zu9d1r-vKnnYr_DIjxO1WfaL9tpY9QPe0M-qAH0AoR-SAvK9VT_wZrac2YwaHnN_DvWcttpCZAhMQ3tKQjogtcfBXeOtnMnYTXLafahgZuTnBzQfm_JgFGhZ1L9YCC/s320/plane49.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_4b/1x1<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhyYlG2gIagIL2D09B5JAXGHJsat_kG7BSvx8kL1coF0tID5m1sVi9jTbANTttC7g_g3T2EceLIW7tP26QBMdQOG5ITVkqAdYkDagmE9oFA3x-gt0W8HbhFon1KTjrnkKqBNSVzr4JSORZo/s1600/plane50.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhyYlG2gIagIL2D09B5JAXGHJsat_kG7BSvx8kL1coF0tID5m1sVi9jTbANTttC7g_g3T2EceLIW7tP26QBMdQOG5ITVkqAdYkDagmE9oFA3x-gt0W8HbhFon1KTjrnkKqBNSVzr4JSORZo/s320/plane50.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_4b/3x3_reduce<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhy1hCz3dkYHFh3IJ-fz9AkDZfSLxIa8XJsaiSQj2T0CrbvEaP4aaMVcMSKRb7-VQxCVurvjyFvg-zMV4jEV8RdEpotNaA2uuei41yqA-0s7zD2pxRAQ_4_A9SEHGYd4Ozh7_SD2TIl-BJ/s1600/plane51.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhy1hCz3dkYHFh3IJ-fz9AkDZfSLxIa8XJsaiSQj2T0CrbvEaP4aaMVcMSKRb7-VQxCVurvjyFvg-zMV4jEV8RdEpotNaA2uuei41yqA-0s7zD2pxRAQ_4_A9SEHGYd4Ozh7_SD2TIl-BJ/s320/plane51.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_4b/3x3<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrWu-XdvgL6cNpCDcSaneLPQ_-GOP5Kna65MAihb8w0X87bj2IclGIbip0v70wWMEfn6f8D25gTOp35ZEAvCtlDO9uEQUM-ieqqErjoLkkGIQNq27Z7GEo2ePT_g8IqzDJuqMQDAX5Ivju/s1600/plane52.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrWu-XdvgL6cNpCDcSaneLPQ_-GOP5Kna65MAihb8w0X87bj2IclGIbip0v70wWMEfn6f8D25gTOp35ZEAvCtlDO9uEQUM-ieqqErjoLkkGIQNq27Z7GEo2ePT_g8IqzDJuqMQDAX5Ivju/s320/plane52.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_4b/5x5_reduce<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgpQSVCtx_26q99xR_E_h_AqvCxS2uaZ53ogtNT1qKAG8nDN7TrhFSo5hBxR3jhTms5QPtNi9ML-lDWO54sgidkscszkKD6hlzesSo5Wd21nRS6GjM6zA1S3vs32jvuZaUNM75PIQwWuiJe/s1600/plane53.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgpQSVCtx_26q99xR_E_h_AqvCxS2uaZ53ogtNT1qKAG8nDN7TrhFSo5hBxR3jhTms5QPtNi9ML-lDWO54sgidkscszkKD6hlzesSo5Wd21nRS6GjM6zA1S3vs32jvuZaUNM75PIQwWuiJe/s320/plane53.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_4b/5x5<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiI_VypkcU0KfGp7r1Im_8zf245HfefLt6FA8p0-XOM62jTGwgaxctpGFl-yE92TByjBglTOEoWuvRTZpBAwKqDcBNQQiblf2RfVwZQ8iMM_YoaxY0IKJw0ZkUvcgVSq6AcGhLB69KDXAKT/s1600/plane54.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiI_VypkcU0KfGp7r1Im_8zf245HfefLt6FA8p0-XOM62jTGwgaxctpGFl-yE92TByjBglTOEoWuvRTZpBAwKqDcBNQQiblf2RfVwZQ8iMM_YoaxY0IKJw0ZkUvcgVSq6AcGhLB69KDXAKT/s320/plane54.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_4b/pool<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj4B2VXUzIbvqZu2zZP1WRLw8yKDZxQymGZC04kj-2HI9y86hwq_VihjvD_P638lND5-Or71c-2Sjpgeb2ePnOLCnw3Vha5wnk1e8_ISkpmW22czhBdFdckPy_VQBLiorOeQPEaJuMcwx6U/s1600/plane55.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj4B2VXUzIbvqZu2zZP1WRLw8yKDZxQymGZC04kj-2HI9y86hwq_VihjvD_P638lND5-Or71c-2Sjpgeb2ePnOLCnw3Vha5wnk1e8_ISkpmW22czhBdFdckPy_VQBLiorOeQPEaJuMcwx6U/s320/plane55.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_4b/pool_proj<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEixHIxwlET_65F6ncj7HfBE92ghDIGO46yVeS0eUoGuur8vNM6Klw-1aJ8l5tBmVqj8GLFzU0zV4fFQotgpMBNmwINhLupTv3tSjQYzt9NXSsERXwNM-n8BiAYps_FyItaFKYk140j3ZrO2/s1600/plane56.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEixHIxwlET_65F6ncj7HfBE92ghDIGO46yVeS0eUoGuur8vNM6Klw-1aJ8l5tBmVqj8GLFzU0zV4fFQotgpMBNmwINhLupTv3tSjQYzt9NXSsERXwNM-n8BiAYps_FyItaFKYk140j3ZrO2/s320/plane56.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_4b/output<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjoi2btRYVDAQNLloPQl5yH-se_DsmQnBhRKDjJwV66uky8PkZ0I22XVEss48pckf4eG9-fqAD6wnSczSBweBsCZveB2Li79hFydh3QaEjLBM0zgDXDIfkBQ8Nv1CeA6b49ZtDh2hrLC1rw/s1600/plane61.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjoi2btRYVDAQNLloPQl5yH-se_DsmQnBhRKDjJwV66uky8PkZ0I22XVEss48pckf4eG9-fqAD6wnSczSBweBsCZveB2Li79hFydh3QaEjLBM0zgDXDIfkBQ8Nv1CeA6b49ZtDh2hrLC1rw/s320/plane61.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_4c/1x1<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj04OcC6N8dxr-KhtClMTinfdc9_tEkyvoXVlukys2-rfilsLz3MBgKog8l2ltR4AkSVs-eGSvLDASMjwALm78x3fYUUxWM0NrSjBSRlEpgVawOWE1zKFPRhx9CuifRIBM4PKsghjIiIq9h/s1600/plane62.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj04OcC6N8dxr-KhtClMTinfdc9_tEkyvoXVlukys2-rfilsLz3MBgKog8l2ltR4AkSVs-eGSvLDASMjwALm78x3fYUUxWM0NrSjBSRlEpgVawOWE1zKFPRhx9CuifRIBM4PKsghjIiIq9h/s320/plane62.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_4c/3x3_reduce<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEivLF-59WhNzCu57lvIU7duDDuPOO6YKk3scieCSKR-j-nJUUn2rijlXhb2y_5hZz95v8DCCLXR5yoJncQa5xAqc_qdPwruPBmaRfAteOsE5mIr5UFoPuk0pVchrUm1FfUw7gqhTZNZRKvd/s1600/plane63.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEivLF-59WhNzCu57lvIU7duDDuPOO6YKk3scieCSKR-j-nJUUn2rijlXhb2y_5hZz95v8DCCLXR5yoJncQa5xAqc_qdPwruPBmaRfAteOsE5mIr5UFoPuk0pVchrUm1FfUw7gqhTZNZRKvd/s320/plane63.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_4c/3x3<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiYBMV_I5OxoaNYzGmwowAF5wpxlUJut12f_QpjPyetY0ZzNiutq1UnJ8T-Tf_aag39drCWKNUFHgvDqoXDeJqkhdkDT2Ktct4sJpkbizOFLv-E1Wtba0NitWyQ21iU_5vnJ2hdte0A8YPF/s1600/plane64.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiYBMV_I5OxoaNYzGmwowAF5wpxlUJut12f_QpjPyetY0ZzNiutq1UnJ8T-Tf_aag39drCWKNUFHgvDqoXDeJqkhdkDT2Ktct4sJpkbizOFLv-E1Wtba0NitWyQ21iU_5vnJ2hdte0A8YPF/s320/plane64.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_4c/5x5_reduce<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjsGhZ2WBoDuEFLRGXGP7RYj_QEH4NjKha0SLprzQZx7FIUxoHkn3vYeL_A3bPoxeZ5Ma3dPpo3tQGKFeorplshlS0ojkt6dJEHnV8FkegGe-7SNzS35jCRQ8gKW2EkRB3p7-5To4MZtnPV/s1600/plane65.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjsGhZ2WBoDuEFLRGXGP7RYj_QEH4NjKha0SLprzQZx7FIUxoHkn3vYeL_A3bPoxeZ5Ma3dPpo3tQGKFeorplshlS0ojkt6dJEHnV8FkegGe-7SNzS35jCRQ8gKW2EkRB3p7-5To4MZtnPV/s320/plane65.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_4c/5x5<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFB_vtM4E5-W0wtwSKCAqkQyxWTWfJBTJ9gX_ohf8B3enwl8xOnqj3sZeHaG6AQFXf4yiHhmeO0i3vrhOB_k-vD0pTYtg7MI13rBlDrAUGQSymB_gSfqP1uj3tocktNuLPoEbU2DIqQXy4/s1600/plane66.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFB_vtM4E5-W0wtwSKCAqkQyxWTWfJBTJ9gX_ohf8B3enwl8xOnqj3sZeHaG6AQFXf4yiHhmeO0i3vrhOB_k-vD0pTYtg7MI13rBlDrAUGQSymB_gSfqP1uj3tocktNuLPoEbU2DIqQXy4/s320/plane66.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_4c/pool<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5htJiwzPlX4eNCN5PP-bhC7HIhVfTZilcfhocskQ2cwfv-uEONxkiQlvQbebj-uV62YGOSF2KHeoYLgw5tSqnP5Q0v7itJ7ZcxwYT0keo_p_am55qyqYnF6NXNmTMzf-NcTgVpRiZDdvT/s1600/plane67.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5htJiwzPlX4eNCN5PP-bhC7HIhVfTZilcfhocskQ2cwfv-uEONxkiQlvQbebj-uV62YGOSF2KHeoYLgw5tSqnP5Q0v7itJ7ZcxwYT0keo_p_am55qyqYnF6NXNmTMzf-NcTgVpRiZDdvT/s320/plane67.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_4c/pool_proj<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPO1hORwemKMpp4_b6-fcgjyL9z0sghu4CPycIbUFwmNz5QKgfxRWhGhgQKRnMj5tsGRBhrYbdJAbrBf2AkDpcn153H_yYybNk05AY1K39yW7EaduYiigLR4_wDu3qX6mHvC4tPzfOFUHf/s1600/plane68.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPO1hORwemKMpp4_b6-fcgjyL9z0sghu4CPycIbUFwmNz5QKgfxRWhGhgQKRnMj5tsGRBhrYbdJAbrBf2AkDpcn153H_yYybNk05AY1K39yW7EaduYiigLR4_wDu3qX6mHvC4tPzfOFUHf/s320/plane68.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_4c/output<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhnAQjCln9q6FKteloCNZO6Qe-4L0H5HVSG2C7hnQG_PYJiOR8YHMiZV7Hx8Jr7udAoI3yWsdCiTDTb3dJGFXf-XDR2SfjQHRCs1pC58RE3tyEJY0vy5tBpr9JGOIOT5erMpKxoNLAOyHhF/s1600/plane73.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhnAQjCln9q6FKteloCNZO6Qe-4L0H5HVSG2C7hnQG_PYJiOR8YHMiZV7Hx8Jr7udAoI3yWsdCiTDTb3dJGFXf-XDR2SfjQHRCs1pC58RE3tyEJY0vy5tBpr9JGOIOT5erMpKxoNLAOyHhF/s320/plane73.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_4d/1x1<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEidmG97QJU1aLPC-UPEokmcD9D4FEQkK9irX3HnGez4kAD29fPn_mHZtz5I5mTOx6fWu7mqDcl9l0R1KnoZc8nilDy2aaNO5tDmLzrv_Igzrtn-s0HFkF7LlqGTwrMPd7isaTeSlnJ8agaw/s1600/plane74.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEidmG97QJU1aLPC-UPEokmcD9D4FEQkK9irX3HnGez4kAD29fPn_mHZtz5I5mTOx6fWu7mqDcl9l0R1KnoZc8nilDy2aaNO5tDmLzrv_Igzrtn-s0HFkF7LlqGTwrMPd7isaTeSlnJ8agaw/s320/plane74.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_4d/3x3_reduce<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9oJgEyt_iALpNlEv98N7G3z8oW-eeADghF6zFXxoJ8Km_FHF8v92UI4vDGu5T-3B7zP8O7w-30UO3wTCE3IlF1XA11d2DTn-VPAzaFoxYxscOKGlzSCUYfRWdDNfQKM91Zd0Te3W62JyE/s1600/plane75.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9oJgEyt_iALpNlEv98N7G3z8oW-eeADghF6zFXxoJ8Km_FHF8v92UI4vDGu5T-3B7zP8O7w-30UO3wTCE3IlF1XA11d2DTn-VPAzaFoxYxscOKGlzSCUYfRWdDNfQKM91Zd0Te3W62JyE/s320/plane75.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_4d/3x3<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEge-lp0BbCoP9lRN5jLoHO53NO-nabx-rj_Jj2dhdP-EnNoxBBYv5aKJyP2mDkhG48ACAto6t3EmM6xYRwiwotCuRXfCRSMqKZxS1xryykrJbxx3DPSKBycqRs2HLRvo9qrTTFTsjLJDtKV/s1600/plane76.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEge-lp0BbCoP9lRN5jLoHO53NO-nabx-rj_Jj2dhdP-EnNoxBBYv5aKJyP2mDkhG48ACAto6t3EmM6xYRwiwotCuRXfCRSMqKZxS1xryykrJbxx3DPSKBycqRs2HLRvo9qrTTFTsjLJDtKV/s320/plane76.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_4d/5x5_reduce<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgcwa2A4T9fEb_p8lx2Kia-mzfXbfQ1Ul63lIxIaXl6ikHriKB8nR6yZcH_hijySWlt8ziLUyy0KD7krhCo4N6eIKMqtN-u3oTP6ASQ5Yo624hVoCgGBy_2eiC3B4TGZPpsPLz3QEVDgCTh/s1600/plane77.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgcwa2A4T9fEb_p8lx2Kia-mzfXbfQ1Ul63lIxIaXl6ikHriKB8nR6yZcH_hijySWlt8ziLUyy0KD7krhCo4N6eIKMqtN-u3oTP6ASQ5Yo624hVoCgGBy_2eiC3B4TGZPpsPLz3QEVDgCTh/s320/plane77.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_4d/5x5<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiugFQnBzipVwNBdUJTP-FkIcaIw9ZqdKOifOPv4QnDM-9WpfkQFPHIRIeEcqmbxj3ePI0NUb_Bta5MD-lTseaNXVboSlj6YByy6zAtq6NRMg_HJhsshhNJuIMfzovqbxLKr_oZd7Cj0zhw/s1600/plane78.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiugFQnBzipVwNBdUJTP-FkIcaIw9ZqdKOifOPv4QnDM-9WpfkQFPHIRIeEcqmbxj3ePI0NUb_Bta5MD-lTseaNXVboSlj6YByy6zAtq6NRMg_HJhsshhNJuIMfzovqbxLKr_oZd7Cj0zhw/s320/plane78.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_4d/pool<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhewD7UpjoLEer1cgIGX3ypY5mhOQyAzSnVB83AaQdh8Dt7mSGLhTqiZA5xtBfybQvhKZZxHgEGbF30pWtQKKtlsIg-i29GPqflcO7Sv6lZkAMhWNBJLb6Uj_4TE_oDgM8YFYQRi5eIBTlo/s1600/plane79.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhewD7UpjoLEer1cgIGX3ypY5mhOQyAzSnVB83AaQdh8Dt7mSGLhTqiZA5xtBfybQvhKZZxHgEGbF30pWtQKKtlsIg-i29GPqflcO7Sv6lZkAMhWNBJLb6Uj_4TE_oDgM8YFYQRi5eIBTlo/s320/plane79.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_4d/pool_proj<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjNMsbvLYPftyEkzLaMKF6wfNCcs8dFg_vioRnnv3MVdDbv_HeCCUbKzEn_fwVQRwzM0ZbrlNU0qFlL0ZuwhTd77_5qGMZIOphHB_7PWFhkgieZ6Rkc3I4Qy2pOHmbpwa_nqiavmdDhLmzQ/s1600/plane80.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjNMsbvLYPftyEkzLaMKF6wfNCcs8dFg_vioRnnv3MVdDbv_HeCCUbKzEn_fwVQRwzM0ZbrlNU0qFlL0ZuwhTd77_5qGMZIOphHB_7PWFhkgieZ6Rkc3I4Qy2pOHmbpwa_nqiavmdDhLmzQ/s320/plane80.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_4d/output<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4DfXHldJmFi2ActksJe_tYJDcgNgWkWS0xPOlyjzY8bTeEyBdCpXfIQwMbWR5t1tk03iRtKbA502pIbbQdEbxpiLYTgPh39WEc7nHaGFdwHaUWDePUpjzk4LXi64-OT1Wox8lewebMRFR/s1600/plane85.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4DfXHldJmFi2ActksJe_tYJDcgNgWkWS0xPOlyjzY8bTeEyBdCpXfIQwMbWR5t1tk03iRtKbA502pIbbQdEbxpiLYTgPh39WEc7nHaGFdwHaUWDePUpjzk4LXi64-OT1Wox8lewebMRFR/s320/plane85.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_4e/1x1<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgOraZeE9U5XionvyvhwZl_fmBwEmPgeBlxd4h_ujsrFbQYNOyClalbUnwV_L4DCxCd0OwzjRrfWiUhQEki7hk7O_kyzmSBI9vfq8ffhue8G1C7aeydF4_o-Q8tKuDrrp_wPqBZB5i5M1SZ/s1600/plane86.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgOraZeE9U5XionvyvhwZl_fmBwEmPgeBlxd4h_ujsrFbQYNOyClalbUnwV_L4DCxCd0OwzjRrfWiUhQEki7hk7O_kyzmSBI9vfq8ffhue8G1C7aeydF4_o-Q8tKuDrrp_wPqBZB5i5M1SZ/s320/plane86.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_4e/3x3_reduce<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhwfqLaH3MT_pP1b-ZypfwmEaH5r7cnVHqIprGbuemJ3DlVRWpkNDl7VY0LQ5JDysMgC-_h0dVGEA5hMWry37pAJ0FjVhXBV4Bgeksu0jzir4ITubcp_ZP7F7YocrDeCXQAMyQ7k1Xi2NI7/s1600/plane87.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhwfqLaH3MT_pP1b-ZypfwmEaH5r7cnVHqIprGbuemJ3DlVRWpkNDl7VY0LQ5JDysMgC-_h0dVGEA5hMWry37pAJ0FjVhXBV4Bgeksu0jzir4ITubcp_ZP7F7YocrDeCXQAMyQ7k1Xi2NI7/s320/plane87.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_4e/3x3<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEihyphenhyphenLg7ob9zol8ODZXSzrkmx7O4lXNKOUo1Ng-THfVtwDGgz48Yk9xoDTsX_iE4MYfwb9-J-bNFTCBj3P1XZAX_hOcP9e4dKZUrXu_qntpxEOie6tfxjyiFCAqj9NxwufvcRrtLcnU13zE3/s1600/plane88.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEihyphenhyphenLg7ob9zol8ODZXSzrkmx7O4lXNKOUo1Ng-THfVtwDGgz48Yk9xoDTsX_iE4MYfwb9-J-bNFTCBj3P1XZAX_hOcP9e4dKZUrXu_qntpxEOie6tfxjyiFCAqj9NxwufvcRrtLcnU13zE3/s320/plane88.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_4e/5x5_reduce<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjxMPXfkVShZiVIXftbwaYbvH25jCo_V38eDJ6Sw8SKQ2Q7ZIOlRFx1tRY0U63zouzL2tXg7PAFrdB4emJnCPXrRz4690FIuJ8rjnOS8KFGFU9xhJszvylEWCrVbFiTU9r5mTO5uL4XCxem/s1600/plane89.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjxMPXfkVShZiVIXftbwaYbvH25jCo_V38eDJ6Sw8SKQ2Q7ZIOlRFx1tRY0U63zouzL2tXg7PAFrdB4emJnCPXrRz4690FIuJ8rjnOS8KFGFU9xhJszvylEWCrVbFiTU9r5mTO5uL4XCxem/s320/plane89.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_4e/5x5<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5ZQ8B_-e6BGpcv1L3-mTVth1eW-xHc3h6TiGZX2aNNlk5ZfNngvi4_iKD8bxOV2VqVGk-oo1YKbl02JouVe5-KgmV1Y4eenMId8x8cWYAJXd1ZZLR1-jTjjYaxvd8Ch0mX_De6i-w7yqq/s1600/plane90.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5ZQ8B_-e6BGpcv1L3-mTVth1eW-xHc3h6TiGZX2aNNlk5ZfNngvi4_iKD8bxOV2VqVGk-oo1YKbl02JouVe5-KgmV1Y4eenMId8x8cWYAJXd1ZZLR1-jTjjYaxvd8Ch0mX_De6i-w7yqq/s320/plane90.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_4e/pool<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhsDKoCMv-DW8MkM-4p3JXKTYG4n4ooMVA2xabGUAfWhByRoYSJzPANiXRXU8L2kgSHjJ92Xpv9fmkOkB1zuQTC0Sq9TG4VOOOjRFGDAh74oe9s10NXOeu_DnMs1OMVPcYw_w6Rn1Xh62K3/s1600/plane91.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhsDKoCMv-DW8MkM-4p3JXKTYG4n4ooMVA2xabGUAfWhByRoYSJzPANiXRXU8L2kgSHjJ92Xpv9fmkOkB1zuQTC0Sq9TG4VOOOjRFGDAh74oe9s10NXOeu_DnMs1OMVPcYw_w6Rn1Xh62K3/s320/plane91.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_4e/pool_proj<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgUJFkXkzv0gQ0UTxr9EOJbpJ_hTHDgTnLUKYKj1pyfec8QptXdxZKfxv-yGC6C1MkjDu53T_tfQCeVa3Z_X_AFLqDqrdrGRzrcwZU6u3VuDhcLDttl8F22SjHC05pzhf_i3ZOUynYAl7Mo/s1600/plane92.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgUJFkXkzv0gQ0UTxr9EOJbpJ_hTHDgTnLUKYKj1pyfec8QptXdxZKfxv-yGC6C1MkjDu53T_tfQCeVa3Z_X_AFLqDqrdrGRzrcwZU6u3VuDhcLDttl8F22SjHC05pzhf_i3ZOUynYAl7Mo/s320/plane92.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_4e/output<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiqOaT9501CB_4U2DN7tQDlLyZJ-iqA4kvdN9sZJiFx-kkgjisHCNOM8_-dxSB-5mfUCWQLE2ho-AEote8O3F-Kbq9dk0jB-PKUtFTOCm0x60_c5NOFJAELkFvwcYqKgEjuXa546QBxu3Ih/s1600/plane93.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiqOaT9501CB_4U2DN7tQDlLyZJ-iqA4kvdN9sZJiFx-kkgjisHCNOM8_-dxSB-5mfUCWQLE2ho-AEote8O3F-Kbq9dk0jB-PKUtFTOCm0x60_c5NOFJAELkFvwcYqKgEjuXa546QBxu3Ih/s320/plane93.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">pool4/3x3_s2<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjmS3ej4hQo2dDJuGdGrEMAcXWY-WJfHD7awtqB7BIsPRvte_GQNvGRSKpnHhl1kmYOy8Qm4v6Id0J3bTzIfC7TiSpbJkC5_zKiyNn9CqjYVZSR3HCGKaUZ_HmNS9Irfpla7oiZ1_PpoOTA/s1600/plane98.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjmS3ej4hQo2dDJuGdGrEMAcXWY-WJfHD7awtqB7BIsPRvte_GQNvGRSKpnHhl1kmYOy8Qm4v6Id0J3bTzIfC7TiSpbJkC5_zKiyNn9CqjYVZSR3HCGKaUZ_HmNS9Irfpla7oiZ1_PpoOTA/s320/plane98.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_5a/1x1<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2vWR7PID10Bl2eLaThKliOJDjg-GBQnsIN_eY_DEzXengC443btnBj0Az_C7kIik5EHNLX_AMRjKPY7aFynhW9_DChzNMCVUMT5jTtgApyYNR_qiUSmiyZPJ11ik9kqkXgwhN20htEld4/s1600/plane99.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2vWR7PID10Bl2eLaThKliOJDjg-GBQnsIN_eY_DEzXengC443btnBj0Az_C7kIik5EHNLX_AMRjKPY7aFynhW9_DChzNMCVUMT5jTtgApyYNR_qiUSmiyZPJ11ik9kqkXgwhN20htEld4/s320/plane99.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_5a/3x3_reduce<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgNtkMvtwfxwJNuYgzkI5ZlN0v8pQWIjkcAWMdaLr0TVSvQyh0LOE1iWgS9WR1cD7fyu-c0QaDsrSKWURvpyI0-HJZJORJRjeKyc7NaFhOOwLcauVRTlnlrVt126ttlfJ73zm2zbEqM65PG/s1600/plane100.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgNtkMvtwfxwJNuYgzkI5ZlN0v8pQWIjkcAWMdaLr0TVSvQyh0LOE1iWgS9WR1cD7fyu-c0QaDsrSKWURvpyI0-HJZJORJRjeKyc7NaFhOOwLcauVRTlnlrVt126ttlfJ73zm2zbEqM65PG/s320/plane100.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_5a/3x3<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhvhtrDUXKLh0xD4moIXxmIKwz6Pxyf35vU6bRftftOuAvHC_1uB6mKaEKGFHeHBh_0u_mIwB1rqCagtlS8LHYyRp5fyBJkmNs5vGOlUjv-0Vp2BLNNigAbusG4mfY7B5xZDBrP55x4zuLe/s1600/plane101.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhvhtrDUXKLh0xD4moIXxmIKwz6Pxyf35vU6bRftftOuAvHC_1uB6mKaEKGFHeHBh_0u_mIwB1rqCagtlS8LHYyRp5fyBJkmNs5vGOlUjv-0Vp2BLNNigAbusG4mfY7B5xZDBrP55x4zuLe/s320/plane101.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_5a/5x5_reduce<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjt5cHe_V7ZrUWYHnH7SqTXbHaM0xpJxS9Ys03xOXsc7d5dfgWQzfkJRhkBvps_c1Wej7h9Hxik4mPA5H51jVbRaBJo1DvB8brLy9kNLgjLP1XBl7IeJcXDSjwa2ikMWwuvRSIKGKQvtaZh/s1600/plane102.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjt5cHe_V7ZrUWYHnH7SqTXbHaM0xpJxS9Ys03xOXsc7d5dfgWQzfkJRhkBvps_c1Wej7h9Hxik4mPA5H51jVbRaBJo1DvB8brLy9kNLgjLP1XBl7IeJcXDSjwa2ikMWwuvRSIKGKQvtaZh/s320/plane102.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_5a/5x5<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJ-Fg5-KRWyZFiPsp-5RsfnkK7jFKq_66LowHOxwvB9J7hCfauwde6UBxhwn3XFTtI5LdbrysSJz7sEIwaXB-Q8193zVBrfRgqkZ76gBdHpwFaLWsKVNh2ykyl2VNQfrqd4_1dshM6HQZL/s1600/plane103.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJ-Fg5-KRWyZFiPsp-5RsfnkK7jFKq_66LowHOxwvB9J7hCfauwde6UBxhwn3XFTtI5LdbrysSJz7sEIwaXB-Q8193zVBrfRgqkZ76gBdHpwFaLWsKVNh2ykyl2VNQfrqd4_1dshM6HQZL/s320/plane103.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_5a/pool<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgG_SvZvwnFYW53R_AW9DLKukeUA2CvBEpf3xmNZOrXBTkp7wUkKvA3_7iIaEdeQvoFF5vgxzt9rkiS_3lAzijbd9yjJtR1n4M_YzlW4ZgICeSCW7w03X_Lk-aymdgFi3H5PZoU2-0qXAIn/s1600/plane104.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgG_SvZvwnFYW53R_AW9DLKukeUA2CvBEpf3xmNZOrXBTkp7wUkKvA3_7iIaEdeQvoFF5vgxzt9rkiS_3lAzijbd9yjJtR1n4M_YzlW4ZgICeSCW7w03X_Lk-aymdgFi3H5PZoU2-0qXAIn/s320/plane104.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_5a/pool_proj<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjwwEUSJPGf07vBPx4CZqTHkgFRwV-dUDKUSwrwkVCBu_QZIf0WLM2awX3YGMlYS9rl4ikfJDm2MyAHgrEnoRmRNN7nezL6fPEscoSFKDV2cNSnUTV_fjrvRotUwfyDKiHv_xp_re8pb2uK/s1600/plane105.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjwwEUSJPGf07vBPx4CZqTHkgFRwV-dUDKUSwrwkVCBu_QZIf0WLM2awX3YGMlYS9rl4ikfJDm2MyAHgrEnoRmRNN7nezL6fPEscoSFKDV2cNSnUTV_fjrvRotUwfyDKiHv_xp_re8pb2uK/s320/plane105.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_5a/output<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiWl3tqqnmuFTG4t5Jb6sFT-9uX8aqClkGF8yOhQRcuKifVJEZ2XFMW0FWsVFSqvkGtGTf7__FnCHlwMzQPaAjj_Edrhwt3rwQzMTe341nXTvSpP_qxlJ53p79g60E44SX6y-E_1yKsNW_e/s1600/plane110.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiWl3tqqnmuFTG4t5Jb6sFT-9uX8aqClkGF8yOhQRcuKifVJEZ2XFMW0FWsVFSqvkGtGTf7__FnCHlwMzQPaAjj_Edrhwt3rwQzMTe341nXTvSpP_qxlJ53p79g60E44SX6y-E_1yKsNW_e/s320/plane110.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_5b/1x1<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjdxlLsnUIlMgZrIleIH9R3LCv7CrwJT179_N61NQc-imYIJHxQPlABUedJsLox1D4rIM6i9tF1aKkepZnGs0hhf_fNu0KHNNoGgbsDlfvUV5e_WxmXn6tVG7zjW1wiauV034EdAeq2K7S2/s1600/plane111.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjdxlLsnUIlMgZrIleIH9R3LCv7CrwJT179_N61NQc-imYIJHxQPlABUedJsLox1D4rIM6i9tF1aKkepZnGs0hhf_fNu0KHNNoGgbsDlfvUV5e_WxmXn6tVG7zjW1wiauV034EdAeq2K7S2/s320/plane111.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_5b/3x3_reduce<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEivNMXVSAygGVoCVtCMZwIAUMq-Rs11Y_kGZ1QFIF0WilODyKQ5ygXc5Faa9hQ8mgyv8uIvKw3OV7_wuFQ8OWhJeJVf3uUa0CbLGQNKZXjLTl5H3LQMPiPX3uGgZ9l_eux7QV6aa-e91Imo/s1600/plane112.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEivNMXVSAygGVoCVtCMZwIAUMq-Rs11Y_kGZ1QFIF0WilODyKQ5ygXc5Faa9hQ8mgyv8uIvKw3OV7_wuFQ8OWhJeJVf3uUa0CbLGQNKZXjLTl5H3LQMPiPX3uGgZ9l_eux7QV6aa-e91Imo/s320/plane112.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_5b/3x3<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjLFufEeB_hXzA6zzO4LFieymlFujdlZ5OOzJCwgd2V1VgUoVkrdVEoFlcstIJuisuz0WInnOoI4Y8I_C-Kj0o2EqNn6n-hcdfRq3fR5lL_J3AZEsczTAOClnXU_Voo9UM28ge8KZckjU9e/s1600/plane113.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjLFufEeB_hXzA6zzO4LFieymlFujdlZ5OOzJCwgd2V1VgUoVkrdVEoFlcstIJuisuz0WInnOoI4Y8I_C-Kj0o2EqNn6n-hcdfRq3fR5lL_J3AZEsczTAOClnXU_Voo9UM28ge8KZckjU9e/s320/plane113.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_5b/5x5_reduce<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjxbw4UNaEbhwhC_CcILSgSCme9d_3uNg8R6JR3vBQ3Qhv5j9tOiq-VBpGAWvDfN2A0D24tcgqxCHvaRV9Bqj2r1_cUfzIo_CWgPo9Hl3SQj-Dqc9bhaxSBbfL5WaqsJatbjOiJH5MgWHax/s1600/plane114.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjxbw4UNaEbhwhC_CcILSgSCme9d_3uNg8R6JR3vBQ3Qhv5j9tOiq-VBpGAWvDfN2A0D24tcgqxCHvaRV9Bqj2r1_cUfzIo_CWgPo9Hl3SQj-Dqc9bhaxSBbfL5WaqsJatbjOiJH5MgWHax/s320/plane114.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_5b/5x5<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEibWr8i3lTFAQFxOiBo5Yzzo_ULwsK7zcb8NkR_kjXjJncyoJY2L-mTnFAk9DH5wxpQLduDSkvx5MZAwb1M7htOnXOZdNVL_tY1DQ_unhbelGAuPU5BaYA_ZYdPQBXdIYHHQo_zg2GdzcK7/s1600/plane115.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEibWr8i3lTFAQFxOiBo5Yzzo_ULwsK7zcb8NkR_kjXjJncyoJY2L-mTnFAk9DH5wxpQLduDSkvx5MZAwb1M7htOnXOZdNVL_tY1DQ_unhbelGAuPU5BaYA_ZYdPQBXdIYHHQo_zg2GdzcK7/s320/plane115.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_5b/pool<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1eN8n65oW1r87tOcisTXu5pFxjS8vI5Cb78xEXUe1udqRu41sgRSqDXilU3EZAJTQ1RLWkp2PLbekGj34TolGIkvyg_pdXHoz72gbjvdGfB4oa2htnv2KwNkXNZ3Svi55wLXd6FMyIsMH/s1600/plane116.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1eN8n65oW1r87tOcisTXu5pFxjS8vI5Cb78xEXUe1udqRu41sgRSqDXilU3EZAJTQ1RLWkp2PLbekGj34TolGIkvyg_pdXHoz72gbjvdGfB4oa2htnv2KwNkXNZ3Svi55wLXd6FMyIsMH/s320/plane116.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_5b/pool_proj<br /></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhASLYAfil8nXbRQp60BpiNDVp6hdckJnLNtzBX_JqfRl_ir6cgacuP-sU-XISw66X5PnD8DIJ9LncA0n1svHD2QrvdloeoPhMcicXAs1FmI5DSLtDuXbbgyk0oQCdjsVFYf8U5HMAcLvhe/s1600/plane117.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhASLYAfil8nXbRQp60BpiNDVp6hdckJnLNtzBX_JqfRl_ir6cgacuP-sU-XISw66X5PnD8DIJ9LncA0n1svHD2QrvdloeoPhMcicXAs1FmI5DSLtDuXbbgyk0oQCdjsVFYf8U5HMAcLvhe/s320/plane117.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.8000001907349px;">inception_5b/output<br /></td></tr>
</tbody></table>
<br />Sam Russellhttp://www.blogger.com/profile/03431137767320819102noreply@blogger.com3tag:blogger.com,1999:blog-7532990877498512287.post-28211983483490846602015-05-02T21:20:00.001-07:002018-01-18T11:11:28.515-08:00Run Heroes2 in a browser<h2>
DOSbox games in a browser!</h2>
<div>
I recently bought a copy of Heroes of Might and Magic 2: The Succession Wars, and I'm expecting that you've done the same (although not necessarily recently, since the game was released last century). You may have seen some of the work that's been done with old abandonware in DOSbox on <a href="https://archive.org/details/softwarelibrary_msdos_games">archive.org</a>, and this is an extension of that.</div>
<h2>
Porting to asm.js with Docker</h2>
<br />
To get started, fire up your dev machine. I'm using an Ubuntu 14.04 server VM in VirtualBox here, with Windows 7 underneath.<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;">git clone <a href="https://github.com/samrussell/emscripten-docker">https://github.com/samrussell/emscripten-docker</a></span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;">cd emscripten-docker</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;">sudo ./build emscripten-ubuntu</span><br />
<br />
That will take some time. While it's running, open another tab and download em-dosbox:<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;">cd emscripten-docker/volume</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;">git clone <a href="https://github.com/dreamlayers/em-dosbox">https://github.com/dreamlayers/em-dosbox</a></span><br />
<br />
When both are finished, go back to the first tab:<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;">sudo ./run</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;">cd /volume/em-dosbox/</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;">./autogen.h</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;">/emsdk_portable/emscripten/master/emconfigure ./configure</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;">make</span><br />
<div>
<br /></div>
<div>
You now have a build of DOSbox. We can then add our Heroes2 files.</div>
<h2>
Porting Heroes2</h2>
<div>
Run the GOG installer and remember the directory it installs to. Find that directory, copy the whole thing, and dump it into the emscripten-docker/volume on your dev machine.</div>
<div>
<br /></div>
<div>
At the time of writing, em-DOSbox is only version 0.70, and it doesn't have CD support. From here on in, I'm assuming that you've patched HEROES2.EXE to work without the CD (although once there is a version of em-DOSbox with CD support feel free to play with imgmount)</div>
<div>
<br /></div>
<div>
In your docker instance, get into the /volume/em-dosbox/src directory. Run the packager as follows:</div>
<div>
<br /></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;">./packager.py heroes2 /volume/homm HEROES2.EXE</span></div>
<div>
<br /></div>
<div>
Then copy out the following 5 files into a folder of your choice:</div>
<div>
<br /></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;">dosbox.html</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;">dosbox.html.mem</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;">dosbox.js</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;">heroes2.data</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;">heroes2.html</span></div>
<div>
<br /></div>
<div>
In that directory, fire up a webserver</div>
<div>
<br /></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;">python -m SimpleHTTPServer 8000</span></div>
<div>
<br /></div>
<div>
Then log in and play</div>
<div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghxAYBKSCBEMZvK8qckEePI1JP6uDuFSUizvsAzW5d0V0MrFwcC6rdi1clY1gB4gYQQGKpYTeKpmRj3UTfMeE2zrMHFeEJEF1skss_rZud2lLDxKUZm_jOf8OOTeFSr8ym2thqeRNe7QsP/s1600/ubisoftaredicks.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="211" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghxAYBKSCBEMZvK8qckEePI1JP6uDuFSUizvsAzW5d0V0MrFwcC6rdi1clY1gB4gYQQGKpYTeKpmRj3UTfMeE2zrMHFeEJEF1skss_rZud2lLDxKUZm_jOf8OOTeFSr8ym2thqeRNe7QsP/s400/ubisoftaredicks.png" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<br /></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;">http://127.0.0.1:8000/heroes2.html</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;"><br /></span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;"><br /></span></div>
<h2>
Next steps</h2>
<div>
I'd be keen to get CDROM support going! But that's a minor thing (and leaving out the CDROM saves us 200MB on the download, and this helps speed things up a little at load time).</div>
<div>
<br /></div>
<div>
I'm also keen to figure out how to get save files and network games running. Save files would presumably need some cleverness pushing stuff back to a server somewhere (integrate with dropbox/drive?), and networking would be interesting too - maybe patch some com support into dosbox and then send packets over twitter or something?</div>
Sam Russellhttp://www.blogger.com/profile/03431137767320819102noreply@blogger.com0tag:blogger.com,1999:blog-7532990877498512287.post-35181406501020988582014-12-20T00:37:00.000-08:002014-12-21T16:06:35.672-08:00How I built my own fountain code<h2>
What problem are we trying to solve?</h2>
<div>
Our phones and computers can only send data in very small chunks at a time. The internet deals with this by breaking up files (webpages, images, videos etc) into chunks and hoping that they can all be reassembled at the other end. This works fairly well, most of the time, and this is the reason that most of the internet runs on <a href="http://en.wikipedia.org/wiki/Transmission_Control_Protocol" rel="nofollow">Transmission Control Protocol (TCP).</a></div>
<div>
<br /></div>
<h2>
Isn't this good enough?</h2>
<div>
TCP has been "good enough" for a few decades now, but it would be nice to have some other options. Here are a couple of scenarios where it would be nice to have something different</div>
<div>
<br /></div>
<h3>
Live content</h3>
<div>
When you're streaming a live event, you can't pause and wait for the download to catch up, or else it's no longer live anymore. TCP isn't suitable for this, as its way of dealing with packet loss is slowing down and resending the lost packets. There are codecs that can handle small amounts of packet loss, but it would be nice if we could trade off more bandwidth to hide any packet loss that might happen</div>
<div>
<br /></div>
<h3>
Content Distribution Networks (CDNs)</h3>
<div>
Chances are that you downloaded this webpage from a different server to the one that I uploaded it to. The global nature of the internet means that requests for content are often directed to a server that's close to you. The way the internet currently works means you can only really download a file from a single server at a time - it's difficult to request it from multiple sources, and there's a chance you'll get multiple copies of the same chunks if you do.</div>
<div>
<br /></div>
<h2>
How does a fountain code help?</h2>
<div>
We can describe TCP as being like a jigsaw puzzle, or a stamp collection. If you have a 1000 piece jigsaw puzzle, and lose a few of the pieces, you can't just get any other random pieces - they have to be the right pieces, or else the puzzle isn't complete. Similarly, the value of a stamp collection is generally in its completeness, rather than the sheer number of stamps there are. 1000 different stamps from a particular collection are valuable, whereas 1000 of the same stamp is rather boring.</div>
<div>
<br /></div>
<h3>
So what is a fountain code?</h3>
<div>
Fountain codes are a type of forward error correction (FEC); specifically, a type of erasure code. An ideal fountain code is like filling up a glass of water at a fountain. You don't care which droplets land in the glass, but you do care about whether the glass is full. The current state-of-the-art fountain codes are based on Luby Transform Codes, which are based on a thing called the XOR operator.</div>
<div>
<br /></div>
<h3>
XOR operators</h3>
<div>
If I have two numbers, 1010, and 1011, I can combine them using an XOR. 1010 xor 1011 = 0001. You line up the two numbers, and if the digits are the same, you write down a 0 - if the digits are different, then you write down a 1. XOR operators are cool because they work just as well in reverse - 1010 xor 0001 = 1011, and 1011 xor 0001 = 1010. We can use this when we break up a file to create extra pieces that can help us out if we get stuck (as a side note, this is how Stereo sound works on FM radio).</div>
<div>
<br /></div>
<h3>
LT codes</h3>
<div>
For example, let's say we have a file that we break up into 5 pieces: a b c d e. We can also send other pieces, such as "a xor c", or maybe "b xor d xor e". If we send all 7, but the other person only receives 5, then they might be able to reconstruct the original message. For example, if you have pieces a, c, d, e, and "b xor d xor e", then you can xor d and e against "b xor d xor e" to extract b - this is the missing piece!</div>
<div>
<br /></div>
<h2>
These sound cool - aren't they good enough?</h2>
<div>
Short answer: you need to pay to use LT codes or Raptor codes (except in New Zealand where software patents are illegal!) (I am not a lawyer btw). They're also the first practical implementation of fountain codes, and it stands to reason that there might be another way to build them that relies on different math.</div>
<div>
<br /></div>
<div>
They can also be a bit picky sometimes about *which* pieces you get. An ideal fountain code would work with any combination of pieces, but may have other issues that make it less practical. There's also a non-zero chance that there's a way cooler code out there, just waiting to be found!</div>
<div>
<br /></div>
<h2>
... so I built my own</h2>
<div>
To quote my old math lecturer Geoff Whittle (congratulations by the way), "last night while I was lying awake thinking about math, I had an idea", so I got up and started writing down notes, and have spent the day re-learning field theory and cutting code. I wanted a system that didn't care at all which pieces you got, and managed to cobble something together in the end using linear equations.</div>
<div>
<br /></div>
<h3>
Simultaneous equations</h3>
<div>
Remember doing these in high school? They usually looked something like this:</div>
<div>
<br /></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">x + 2y = 25</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">3x + 4y = 9</span></div>
<div>
<br /></div>
<div>
The idea was that as long as you had as many equations as unknowns, you could usually solve the equations. The way you'd solve this was something along the lines of</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">(1) x + 2y = 25</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">(2) 3x + 4y = 9</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;">(subtract (1) from (2) three times)</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;">(3a) 0x - 2y = -66</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;">(3b) -2y = -66</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;">(divide by -2)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">(4) y = 33</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">(substitute y for 33 in eqn 1)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">(5a) x + 2*33 = 25</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">(5b) x + 66 = 25</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">(subtract 66)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">(6a) x + 66 - 66 = 25 - 66</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">(6b) x = -41</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">x = -41</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">y = 33</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"><br /></span></div>
</div>
<div>
<br /></div>
<div>
There's no reason why x and y can't stand for multiple things though. Let's try this again:</div>
<div>
<br /></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">x + 2y = 25, 49, 10, 58, 23, 95, 23, 47</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">3x + 4y = 9, 12, 67, 34, 23, 67, 98, 23</span></div>
<div>
<br /></div>
<div>
This looks kinda odd, but it still works. In addition, we've already done the work solving the first pair (25 and 9), so instead of solving again, we can just run the 6-steps for each of the other pairs and they'll solve too.</div>
<div>
<br /></div>
<div>
We can make this bigger if you like:</div>
<div>
<br /></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">a + 31b + 4c + 2d + 45e + 5f = 12, 45, 65, 29 ..</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">3a + 24b + 2c + 23d + 23e + 11f = 56, 234, 92, 30 ..</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">6a + 45b + 4c + 65d + 56e + 89f = 39, 12, 6, 3 ..</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">5a + 6b + 34c + 4d + 78e + 56f = 34, 12, 67, 45 ..</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">23a + 23b + 12c + 43d + 12e + 23f = 4, 79, 20, 3 ..</span></div>
<div>
<br /></div>
<div>
It's worth pointing out that these equations are arbitrarily chosen - the receiver needs to get 5 of them to reconstruct the message, but if they miss a piece then we just choose a random new set of coefficients, calculate the answers, and send the packet down the line.</div>
<div>
<br /></div>
<h3>
Results</h3>
<div>
It works! If you want 10 pieces, you can rebuild the message from *any* 10 pieces - I've run this about 50 times today and have yet to see decoding fail.</div>
<h3>
</h3>
<h3>
Improvements</h3>
<div>
You'll notice I haven't used any fractions or decimal points. There's not enough room here to get into this today, but I've used <a href="http://www.ee.unb.ca/cgi-bin/tervo/galois3.pl?p=5">Galois Fields</a> of order 2^8 with generator polynomial x^8 + x^4 + x^3 + x + 1. In short, if you redefine what addition and multiplication mean, and keep the numbers in the range of 0-255 (one byte, 8 bits) then you can avoid fractions while speeding up the calculations.</div>
<h3>
</h3>
<h3>
Problems</h3>
<div>
There's quite a bit of overhead involved. Each packet has to have the coefficients on the front, and needs one coefficient for each piece you've split the message into. If you have 100 pieces, you need 100 bytes of overhead on the front. If you have 1000 pieces, you have 1000 bytes of overhead on the front. If we keep in mind that an internet packet only holds about 1400 bytes, it means we have some serious limits here. If we want to send a 100KB message, we need to split it into about 100 pieces, and each piece has about 10% coding overhead. A 1MB message is out of the question. There are ways around this, where a 1GB file gets split up into 10,000 x 100KB messages, and you dump pieces onto the wire and the receiver lets you know once they've reconstructed parts, but this wasn't quite the ideal fountain code that I was hoping for.</div>
<div>
<br /></div>
<div>
In addition to this, the reference implementation really struggles to do the calculations once you get past 20-50 pieces. I suspect that most of this is due to the code being in python/numpy, and there are plenty of optimisations that could be made.<br />
<br />
<b>update 21/12/2014</b><br />
<b><br /></b>
The coefficients are random anyway, so I've changed things to make it just transmit a 4-byte seed for python's random.randint(). Now it'll handle any number of pieces with constant byte overhead, but 130 (for a 175KB file) pieces still takes a bit of effort to process (read: 2 minutes at each end to encode/decode). Given the encode time is similar to the decode time, I suspect the slowness is caused by the matrix multiplication code, or some double-copying of the GF(2^8) multiplication table, rather than my Gauss-Jordan implementation being dodgy. This gives me hope that I can speed things up by a few orders of magnitude and start testing with multiple MB files soon!<br />
<br />
<b>update 22/12/2014</b><br />
<br />
It's been brought to my attention that this work has previously b<span style="font-family: inherit;">een covered by Muriel M<span style="background-color: white; line-height: 20px;">édard at MIT. Looks like I have a bit more reading to do :)</span></span></div>
<h3>
Future work</h3>
<div>
There's still an ideal fountain code out there, waiting to be found. I sincerely hope somebody finds my work useful, but in any case, it's been a good day's work.</div>
<h3>
</h3>
<h3>
Code pls</h3>
<div>
<a href="https://github.com/samrussell/rcode">Here's the code on github</a></div>
Sam Russellhttp://www.blogger.com/profile/03431137767320819102noreply@blogger.com1tag:blogger.com,1999:blog-7532990877498512287.post-26944129332973615892014-11-30T13:17:00.003-08:002014-11-30T13:17:58.599-08:00Bootstrapping LXCs behind an APT proxyHad some fun getting lxc-create to do its stuff this morning, the /etc/default/lxc config file has half a hint but not a whole one:<br />
<br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"># MIRROR to be used by ubuntu template at container creation:</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"># Leaving it undefined is fine</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">#MIRROR="http://archive.ubuntu.com/ubuntu"</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"># or </span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">#MIRROR="http://<host-ip-addr>:3142/archive.ubuntu.com/ubuntu"</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><br /></span>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"># LXC_AUTO - whether or not to start containers symlinked under</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"># /etc/lxc/auto</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">LXC_AUTO="true"</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><br /></span>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"># Leave USE_LXC_BRIDGE as "true" if you want to use lxcbr0 for your</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"># containers. Set to "false" if you'll use virbr0 or another existing</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"># bridge, or mavlan to your host's NIC.</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">USE_LXC_BRIDGE="true"</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><br /></span>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"># If you change the LXC_BRIDGE to something other than lxcbr0, then</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"># you will also need to update your /etc/lxc/lxc.conf as well as the</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"># configuration (/var/lib/lxc/<container>/config) for any containers</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"># already created using the default config to reflect the new bridge</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"># name.</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"># If you have the dnsmasq daemon installed, you'll also have to update</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"># /etc/dnsmasq.d/lxc and restart the system wide dnsmasq daemon.</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">LXC_BRIDGE="lxcbr0"</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">LXC_ADDR="10.0.3.1"</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">LXC_NETMASK="255.255.255.0"</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">LXC_NETWORK="10.0.3.0/24"</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">LXC_DHCP_RANGE="10.0.3.2,10.0.3.254"</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">LXC_DHCP_MAX="253"</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><br /></span>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">LXC_SHUTDOWN_TIMEOUT=120</span><br />
<br />
The line that it's missing is "you'll also need to set the SECURITY_MIRROR field or else it dies halfway" - so add the following to your /etc/default/lxc and you're good to go:<br />
<br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">MIRROR="http://IP-OF-PROXY:3142/archive.ubuntu.com/ubuntu"</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">SECURITY_MIRROR="http://IP-OF-PROXY:3142/security.ubuntu.com/ubuntu"</span><br />
<div>
<br /></div>
<br />Sam Russellhttp://www.blogger.com/profile/03431137767320819102noreply@blogger.com0tag:blogger.com,1999:blog-7532990877498512287.post-8099211344983377842014-11-25T13:18:00.001-08:002014-11-25T13:18:52.173-08:00How to install Ubuntu Server 14.04 on a MacWe have an old Mac tower in the office that I've been using as a headless server, and last time around I couldn't even get 12.04 server to install - I needed to install 12.04 desktop.<br />
<br />
This time around, the Ubuntu server 14.04 install works fine... except it hangs at boot<br />
<br />
Looking at the output, it has an issue with plymouth-upstart-bridge, then tries to mount swap, and then it does nothing...<br />
<br />
I came across <a href="http://blog.jamesrhall.com/2014/04/ubuntu-server-1404-fun.html">this post</a> that had the answer - booting will work if you boot in recovery mode (grub -> advanced options -> ubuntu recovery) and then resume the boot. The permanent fix is to add "nomodeset" to grub.<br />
<br />
Instructions from the beginning<br />
<br />
<br />
<ol>
<li>Copy ubuntu 14.04 amd64 server iso onto a USB drive (sudo dd if=ubuntu-14.04-server-amd64.iso of=/dev/sdX bs=1M) (*don't blame me* if you accidentally override your main harddrive)</li>
<li>Plug usb key into server, boot up (if it doesn't boot off USB you may need to hold down ALT to get the magic screen and then choose EFI USB boot)</li>
<li>Run install like you normally would</li>
<li>Find that it hangs on boot</li>
<li>Boot again but in recovery mode (at GRUB choose "advanced options" and boot into recovery mode)</li>
<li>Log in</li>
<li>sudo vim /etc/default/grub</li>
<li>Edit the line GRUB_CMDLINE_LINUX_DEFAULT="" to say GRUB_CMDLINE_LINUX_DEFAULT="nomodeset"</li>
<li>Save file</li>
<li>sudo update-grub</li>
<li>Reboot</li>
<li>*magic it works yay*</li>
</ol>
Sam Russellhttp://www.blogger.com/profile/03431137767320819102noreply@blogger.com0tag:blogger.com,1999:blog-7532990877498512287.post-43892425542662258422014-08-05T20:01:00.001-07:002014-08-05T20:28:08.439-07:00Private servers on EC2<h2>
Build all the servers (or, how to setup a secure-ish private minecraft server on EC2)</h2>
<div>
At the time of writing, Amazon EC2 gives you a free virtual machine for the first 12 months of a new account. It's not huge, but it has a CPU and 1GB of RAM, and that gives you a few possibilities. A VM in the cloud is cool, but there's an obvious downside to not having it in your own house/office/parents' basement</div>
<div>
<br /></div>
<h3>
Open servers on the internet</h3>
<div>
If you want to do anything cool with your VM, chance are you'll end up opening ports, and introducing security holes. You may say that you're not worried about this, that your server isn't super important, but if someone can own your VM, then they're one step closer to breaking into your own computer, and that's a bad thing.</div>
<div>
<br /></div>
<div>
In short, you need a way to lock things down, and that's what we're going to do here. But first, let's spin up an EC2 VM<br />
<br />
<h3>
Ubuntu VMs on EC2</h3>
<br />
Go to the <i>Compute</i> menu, and click on <i>Launch Instance</i><br />
<i><br /></i>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEja1mjjF_L6GQ_uVMM06zBylgmo_KtbBPZ2uoxwAdW7nh2d5OZ72xDMl3eyK5dhSAsz4G9X4ngXG-k50hSAChCLu0lt7v49A4boJau494TJn0uSGXZGUYWOkJHrsZgrKMyKlsb2ehF6L2WI/s1600/Screen+Shot+2014-08-05+at+9.59.48+pm.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEja1mjjF_L6GQ_uVMM06zBylgmo_KtbBPZ2uoxwAdW7nh2d5OZ72xDMl3eyK5dhSAsz4G9X4ngXG-k50hSAChCLu0lt7v49A4boJau494TJn0uSGXZGUYWOkJHrsZgrKMyKlsb2ehF6L2WI/s1600/Screen+Shot+2014-08-05+at+9.59.48+pm.png" height="61" width="400" /></a></div>
<br />
Check the <i>Free tier only</i> box, and click on the Ubuntu 64 bit VM<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgWkawIlPu8MLd1nTvGno9EToUGn4wkBV5u8pZSLWrir4MjteL8pZuLERkAUuRewO4RJu5KkMoZNE2CK2CxZs_ej3w5eGzPbzaHxBdPktnBGCTUAv8Hgz230JwEJblTZfFIdL87_AQYsZxQ/s1600/Screen+Shot+2014-08-05+at+9.59.59+pm.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgWkawIlPu8MLd1nTvGno9EToUGn4wkBV5u8pZSLWrir4MjteL8pZuLERkAUuRewO4RJu5KkMoZNE2CK2CxZs_ej3w5eGzPbzaHxBdPktnBGCTUAv8Hgz230JwEJblTZfFIdL87_AQYsZxQ/s1600/Screen+Shot+2014-08-05+at+9.59.59+pm.png" height="185" width="400" /></a></div>
<br />
You'll see the green highlighted <i>free tier eligible</i> text next to the smallest VM, select that and click <i>Next: Configure Instance Details</i><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgfCve7BuP2-kS9CDgTrkxfCYnIVQYs6U4K6U3mz6qI3Sb7WSZdpt8DeR1PTmnPjUXUv8K2ovQJRl8PrcVNU5giKEgTlUds4jgbkAOaxyWtDo7_FLXP29hU0LESzOb0Yv5sM_xTtptZbOgm/s1600/Screen+Shot+2014-08-05+at+10.00.05+pm.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgfCve7BuP2-kS9CDgTrkxfCYnIVQYs6U4K6U3mz6qI3Sb7WSZdpt8DeR1PTmnPjUXUv8K2ovQJRl8PrcVNU5giKEgTlUds4jgbkAOaxyWtDo7_FLXP29hU0LESzOb0Yv5sM_xTtptZbOgm/s1600/Screen+Shot+2014-08-05+at+10.00.05+pm.png" height="192" width="400" /></a></div>
<br />
Leave as is and click next<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh3eHLgGHk0I_pcJWiYiDjpzKgU90MVkHnGztYglNyMTLvSZH_hneQ1NtUqfxbie5eCjMukqNwMi3BpXnIu1Hix3Mec5JmsjCT-gSnYBhvKjktZxu7hm1XhEeBKgbXhpYBWoRPV-CBGpz4k/s1600/Screen+Shot+2014-08-05+at+10.00.20+pm.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh3eHLgGHk0I_pcJWiYiDjpzKgU90MVkHnGztYglNyMTLvSZH_hneQ1NtUqfxbie5eCjMukqNwMi3BpXnIu1Hix3Mec5JmsjCT-gSnYBhvKjktZxu7hm1XhEeBKgbXhpYBWoRPV-CBGpz4k/s1600/Screen+Shot+2014-08-05+at+10.00.20+pm.png" height="196" width="400" /></a></div>
<br />
Nothing to see here, click next<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHjzGDy06PDcfJx7zWKk9IbVAUameI-PYjWjwPCUMgB1hocNKOzM9Bqjurl3Jcb15DQOQ0nN01HQrIWeOSM63vdovR5YTgjmQMzLkcLcn73ZHQji4LmRlbnoC3Nx4hoNwXo_yLDvHNaz4B/s1600/Screen+Shot+2014-08-05+at+10.00.26+pm.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHjzGDy06PDcfJx7zWKk9IbVAUameI-PYjWjwPCUMgB1hocNKOzM9Bqjurl3Jcb15DQOQ0nN01HQrIWeOSM63vdovR5YTgjmQMzLkcLcn73ZHQji4LmRlbnoC3Nx4hoNwXo_yLDvHNaz4B/s1600/Screen+Shot+2014-08-05+at+10.00.26+pm.png" height="192" width="400" /></a></div>
<br />
You can add tags if you want, but we don't need them for anything in particular right now<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhGCETmlKijtvI7hY_f53x4cImfjbTwXjBNbm1Yj3RpCQjBrhg5a8tBjjIswFvqAZpmvq81pWZ_WTdgbdcFOvBn74-8tHTG_y9-6lgnSFU1pk52R4tNf332Q2VdV2CpKMBiKqQ1Ut1Qfbc-/s1600/Screen+Shot+2014-08-05+at+10.00.35+pm.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhGCETmlKijtvI7hY_f53x4cImfjbTwXjBNbm1Yj3RpCQjBrhg5a8tBjjIswFvqAZpmvq81pWZ_WTdgbdcFOvBn74-8tHTG_y9-6lgnSFU1pk52R4tNf332Q2VdV2CpKMBiKqQ1Ut1Qfbc-/s1600/Screen+Shot+2014-08-05+at+10.00.35+pm.png" height="197" width="400" /></a></div>
<br />
You'll want to make some adjustments to the security groups - click <i>Add Rule</i> and make a custom TCP rule opening up port 1723 - this will allow PPTP VPN connections.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhNW6riqp5Y_jbOe40Qss7xH86txkTNitw1adlkUu2tQ7EBxPmSkmzioLnohHLFrWUr6G18a6KehCg0xokQJpdJ2JxnoJ08h4ofs_5dYU1mGsKQYIZxPwA7i4_5pCdjihXBJv7VRptPcc9T/s1600/Screen+Shot+2014-08-05+at+10.00.41+pm.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhNW6riqp5Y_jbOe40Qss7xH86txkTNitw1adlkUu2tQ7EBxPmSkmzioLnohHLFrWUr6G18a6KehCg0xokQJpdJ2JxnoJ08h4ofs_5dYU1mGsKQYIZxPwA7i4_5pCdjihXBJv7VRptPcc9T/s1600/Screen+Shot+2014-08-05+at+10.00.41+pm.png" height="190" width="400" /></a></div>
<br />
Your config should look like mine here<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgD4sH54rrWpkg6R_Y3ZChSyqCo288Ac9S433qQyOasAVir7uGbnuG-ZyEbBGQApoKQn_lZgSDOc1G_QoYkJ70wWLFL-Uxdy802oAVHn15xrxhseH_fudPWFttR1Xhubd1JJOMhocvSsEJo/s1600/Screen+Shot+2014-08-05+at+10.01.09+pm.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgD4sH54rrWpkg6R_Y3ZChSyqCo288Ac9S433qQyOasAVir7uGbnuG-ZyEbBGQApoKQn_lZgSDOc1G_QoYkJ70wWLFL-Uxdy802oAVHn15xrxhseH_fudPWFttR1Xhubd1JJOMhocvSsEJo/s1600/Screen+Shot+2014-08-05+at+10.01.09+pm.png" height="192" width="400" /></a></div>
<br />
And now you're good to go, click Launch and head to the final step<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5uR-oAgpNElPedKveqEdbawAAeXBqzDJ4jXGw3YlQng3F1wOU-YWBRkqJYzhZ9rmeBCWYfYLJdzxGQMUzdED_3qfN39Fe8kZCJxIh471S9s6d5IOI_KP84AnBh0XuK019V0o5anTY24Q-/s1600/Screen+Shot+2014-08-05+at+10.01.17+pm.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5uR-oAgpNElPedKveqEdbawAAeXBqzDJ4jXGw3YlQng3F1wOU-YWBRkqJYzhZ9rmeBCWYfYLJdzxGQMUzdED_3qfN39Fe8kZCJxIh471S9s6d5IOI_KP84AnBh0XuK019V0o5anTY24Q-/s1600/Screen+Shot+2014-08-05+at+10.01.17+pm.png" height="195" width="400" /></a></div>
<br />
Make a name for your keypair and download it<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj4QQIMdTwBeE5BbccX7RP0bvfO8rjN3zp8qYQ2Qz209zAgFmcaLrt8UhmCqWt5cTs3x8xRqnYClIyBEBgW2oodADO65DnaDp7lbxgvMiVbq6v97YdSvxYPrpIygeP_Bas4f9RxAZBL8dC5/s1600/Screen+Shot+2014-08-05+at+10.01.34+pm.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj4QQIMdTwBeE5BbccX7RP0bvfO8rjN3zp8qYQ2Qz209zAgFmcaLrt8UhmCqWt5cTs3x8xRqnYClIyBEBgW2oodADO65DnaDp7lbxgvMiVbq6v97YdSvxYPrpIygeP_Bas4f9RxAZBL8dC5/s1600/Screen+Shot+2014-08-05+at+10.01.34+pm.png" height="296" width="400" /></a></div>
<br />
<br />
Annnnnnnd you're all good to go!<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjLTHLJbCEJ6s967gnhY_-epBHUXv0blP79EGlsn-SUjUco23q8yXlHP2k-NiJGsAgE1HqBc69FXhazg-H0dHiQkuMXqUWqQiNgW2y3LyusqLMdBKm4wUS-86FN39ahOqxqlvQDWUQAykVr/s1600/Screen+Shot+2014-08-05+at+10.01.44+pm.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjLTHLJbCEJ6s967gnhY_-epBHUXv0blP79EGlsn-SUjUco23q8yXlHP2k-NiJGsAgE1HqBc69FXhazg-H0dHiQkuMXqUWqQiNgW2y3LyusqLMdBKm4wUS-86FN39ahOqxqlvQDWUQAykVr/s1600/Screen+Shot+2014-08-05+at+10.01.44+pm.png" height="228" width="400" /></a></div>
<br />
To log in (instructions for OSX/Linux, windows users can figure out how to do this in PuTTY themselves), use the -i switch to load the private key (make sure you chmod it to 600), and log in with the user ubuntu. On the "compute" screen you'll be able to see the IP for your server, log in as follows:<br />
<br />
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;">ssh -i keyfile.pem ubuntu@IP</span><br />
<br />
And you're in! Feel free to have a play with this as is, or move onto the next step - setting up containers.</div>
<div>
<br /></div>
<h2>
Linux containers (LXC)</h2>
<div>
<h3>
Making them work</h3>
LXCs are the secret sauce that'll let you make faux-VMs to put things in. Install LXC with the following:<br />
<br />
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;">sudo apt-get update</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;">sudo apt-get install lxc</span><br />
<br />
This will load up everything you need to build an LXC. To build your LXC, use the following. We'll call our container "minecraft", for no particular reason:<br />
<br />
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;">sudo lxc-create -t ubuntu -n minecraft</span><br />
<br />
This will take <b>a long time,</b> so get yourself a coffee or three. This only happens the first time, as it sets up the template - subsequent LXCs will take seconds when you make them.<br />
<br />
Once this finishes, start it up and log in (CTRL+A followed by the Q key will get you out of the console)<br />
<br />
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;">sudo lxc-start -d -n minecraft</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;">sudo lxc-console -n minecraft</span><br />
<br />
Here's your container! If for some reason, you <i>were</i> wanting to install minecraft, you would install the JDK and download the <a href="https://minecraft.net/download">minecraft JAR file</a> from the appropriate website. The below lines will install version 1.7.10, you'll probably want whatever the latest version is.<br />
<br />
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;">sudo apt-get install default-jdk</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;">wget https://s3.amazonaws.com/Minecraft.Download/versions/1.7.10/minecraft_server.1.7.10.jar</span><br />
<br />
<h3>
Making things a little nicer</h3>
</div>
<div>
Your LXCs will use DHCP when you spin them up, which is great for getting things going at first, but we want a little bit more control. Go back to the host machine (CTRL+A, then Q, if you aren't in it already) and open up /etc/default/lxc-net.conf and change the DHCP settings so we can open up some space - change the settings in there to the following:<br />
<br />
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;">LXC_BRIDGE="lxcbr0"</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;">LXC_ADDR="10.0.3.1"</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;">LXC_NETMASK="255.255.255.0"</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;">LXC_NETWORK="10.0.3.0/24"</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;">LXC_DHCP_RANGE="10.0.3.200,10.0.3.240"</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;">LXC_DHCP_MAX="40"</span><br />
<br />
We'll also want to give our LXC a static IP, so get back into your LXC<br />
<br />
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;">sudo lxc-console -n minecraft</span><br />
<br />
and edit /etc/network/interfaces (if you're still in the host you can edit this at /var/lib/lxc/minecraft/rootfs/etc/network/interfaces if you like). Remove the line<br />
<br />
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;">iface eth0 inet dhcp</span><br />
<br />
and replace with the following<br />
<br />
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;">iface eth0 inet static</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;"> address 10.0.3.250</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;"> netmask 255.255.255.0</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;"> gateway 10.0.3.1</span><br />
<br />
Save and close, and power off your container for now<br />
<br />
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;">sudo poweroff</span><br />
<br />
This is all we need from the LXC for now, let's set up the VPN.<br />
<br />
<h2>
PPTP VPNs</h2>
</div>
<div>
We're using PPTP today because you can get it working in minutes, and it has native clients in Ubuntu and OSX. As per the <a href="https://help.ubuntu.com/community/PPTPServer">instructions on the Ubuntu website</a>, we'll install as follows:</div>
<div>
<br /></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;">sudo apt-get install pptpd</span></div>
<div>
<br /></div>
<div>
Then edit /etc/pptpd.conf, scroll to the bottom, and set up addresses for clients. I've done this as follows:</div>
<div>
<br /></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;">localip 10.0.3.1</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;">remoteip 10.0.3.2-22</span></div>
<div>
<br /></div>
<div>
Open /etc/ppp/pptpd-options, and add DNS</div>
<div>
<br /></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;">ms-dns 8.8.8.8</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;">ms-dns 8.8.4.4</span></div>
<div>
<br /></div>
<div>
Then edit /etc/ppp/chap-secrets and set up accounts. This shouldn't need to be said, but just in case security isn't clear, DO NOT USE THESE LOGINS AS EVERYONE ON THE INTERNET KNOWS WHAT THEY ARE NOW</div>
<div>
<br /></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;">user1 * password1 *</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;">user2 * password2 *</span></div>
<div>
<br /></div>
<div>
Now restart the PPTP server</div>
<div>
<br /></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;">/etc/init.d/pptpd restart</span></div>
<div>
<br /></div>
<div>
And give it a shot!</div>
<div>
<br /></div>
<h2>
Putting it all together</h2>
<div>
Weird stuff can happen if you haven't shut everything down, so the safest way to apply your LXC settings is reboot the whole VM</div>
<div>
<br /></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;">reboot</span></div>
<div>
<br /></div>
<div>
Now log back in, start up your LXC, and then try to log in from the outside. Connect to your server with your PPTP account, then once the VPN is up, try to ping your LXC on 10.0.3.250. If you've installed a minecraft server on it, log into the LXC and fire it up with the following:</div>
<div>
<br /></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;">java -Xms512M -Xmx1G -jar minecraft_server.1.7.10.jar nogui</span></div>
<div>
<br /></div>
<div>
Edit the newly-generated eula.txt to say true, start up again, and test it out... remember to use the LXC's internal IP (10.0.3.250) when connecting though!</div>
<h3>
Troubleshooting</h3>
<br />
<ul>
<li>Try turning it off and on again. I am not joking.</li>
<li>Make sure you're fully upgraded (apt-get upgrade/apt-get dist-upgrade) - LXC has dependencies on newer versions of certain code, but doesn't automatically upgrade them for you.</li>
</ul>
<br />
<h2>
What have we done?!</h2>
<div>
What's the point in putting a VM inside a VM in the cloud? The reason you might consider this, is because while cloud resources are cheap and plentiful, they're also hard to configure properly to allow full access to you, but completely lock out everyone else. I can't make any guarantees on the security of this - LXC is still new, and there are further things we can do to lock down the internal bridge - but I hope this has been a useful start to get people thinking about bridging out into private servers in public clouds such as EC2.</div>
Sam Russellhttp://www.blogger.com/profile/03431137767320819102noreply@blogger.com0tag:blogger.com,1999:blog-7532990877498512287.post-18638608395348124952014-05-20T13:33:00.000-07:002014-05-20T13:33:12.086-07:0010G > 1G<h2>
10Gb/s ain't what it used to be</h2>
<div>
It was only a few years ago that 10Gb/s kit cost 10's of thousands of dollars and needed massive XenPaks to plug in as optics. It's now 2014, 10Gb/s SFPs cost about $200 each, and the closest I get to a XenPak is the broken one I use as a bottle opener.</div>
<div>
<br /></div>
<div>
Because it's so cheap, it's a no-brainer to put 10Gb/s NICs in your servers, but there's no guarantee that your network can support 10Gb/s the whole way through. You might think that a 1Gb/s bottleneck in your network isn't a big deal, and that TCP can fumble around and find the top speed for your connection, but you might be disappointed to hear that it's not that easy.</div>
<div>
<br /></div>
<h2>
TCP is dumb</h2>
<div>
TCP doesn't have any parameters, internal or external, for how fast it's sending data. It has a window of how much unaccounted data has been sent, and this window moves along as acknowledgements are received. The size of this window sets an upper bound on the average speed (based on latency and packet loss, <a href="http://www.switch.ch/network/tools/tcp_throughput/">feel free to explore this</a>), but not on the maximum speed. This becomes a problem as bandwidth and latency both increase.</div>
<div>
<br /></div>
<div>
<h2>
Long fat networks</h2>
</div>
<div>
The TCP window keeps track of every byte "in flight" - in other words, all data that has been sent and not acknowledged. It can't send any more data until the first lot of data has been acknowledged, and it needs a buffer (window) to track this. The smallest this buffer can be is latency x bandwidth, and this number can get very big very quickly. If you're trying to send at 1Gb/s to a destination 160ms away, you need a window of 20MB - if you want to do this at 10Gb/s, you need a window of 200MB! Compared to the 128GB flash drives that clip onto your keyring, this doesn't seem like a huge amount, but to the switches and routers in your network, this is a lot to soak up if your traffic has to go across a slow part of the network</div>
<h2>
How 10 goes into 1</h2>
<div>
If your sender and receiver have 10Gb/s connections, but the network has a 1Gb/s segment in the middle, you can run into interesting problems. With 160ms of latency in the way, your sender dumps 20MB onto the network at 10Gb/s - in the time it takes to arrive at the start of the 1Gb/s segment, the 1Gb/s segment can only send 2MB - leaving 18MB to be dealt with. If you have big enough buffers, then this will eke out into the network at 1Gb/s and everything will be fine!</div>
<div>
<br /></div>
<div>
However, 20MB is a big buffer - it holds 160ms of data. We've all seen buffer bloat (when buffers fill up and stay full and add extra latency to the network), and hear about it being a bad thing, but this is an instance where buffers are *very* important. If you have no buffers, your TCP stream starts up and immediately drops 90% of its packets, and things go very bad, very fast.</div>
<div>
<br /></div>
<h2>
Labbing this up</h2>
<div>
You can simulate this yourself between two Linux machines with tc and iperf. First, plug them into each other at 10Gb/s, make sure they're tuned (net.ipv4.tcp_rmem/wmem need to have the last parameter set to about 64MB), and test between them. Assuming sub-millisecond latency, you should see very close to 10Gb/s of TCP throughput (if not, the servers are mistuned, or underpowered).</div>
<div>
<br /></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">box1: iperf -i1 -s</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">box2: iperf -i1 -c box1 -t300</span></div>
<div>
<br /></div>
<div>
Looking good? If not, you're out of luck in this instance - TCP tuning is outside the scope of this post, <a href="http://fasterdata.es.net/">go and ask ESnet</a> what to do.</div>
<div>
<br /></div>
<div>
Assuming this is all working, we'll add some latency as an egress filter on box1, and see what happens</div>
<div>
<br /></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">sudo tc qdisc add dev eth0 netem delay 50ms limit 10000</span></div>
<div>
<br /></div>
<div>
Try the iperf again - is it still working well? If you're not averaging 7-8Gb/s then you might want to do some tuning, and come back when it's looking better.</div>
<div>
<br /></div>
<div>
Now we've got a known-good at 50ms, let's try simulating a 1Gb/s bottleneck in the network. Apply an ingress policer to box2 as follows:</div>
<div>
<br /></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">sudo tc qdisc add dev eth0 handle ffff: ingress</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">sudo tc filter add dev eth0 parent ffff: protocol ip prio 1 u32 match ip src 0.0.0.0/0 police rate 1000mbit burst 100k mtu 100k drop flowid :1</span></div>
<div>
<br /></div>
<div>
Try your iperf again - how does it look? When I tested this, I couldn't get more than 10Mb/s - something is seriously wrong! Let's try and send some UDP through to see what's happening</div>
<div>
<br /></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">box1: iperf -i1 -s -u -l45k</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">box2: iperf -i1 -u -l45k -c box1 -t300 -b800m</span></div>
<div>
<br /></div>
<div>
Odd... we can send 800Mb/s of UDP with little or no packet loss, but can't get more than 20Mb/s of TCP?!</div>
<div>
<br /></div>
<div>
The fix for this is adding a shaper to make sure nothing gets dropped by the policer. We can add this on box1 in this instance as follows:</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">sudo tc qdisc del dev eth0 root</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">sudo tc qdisc add dev eth0 root handle 1: tbf rate 900mbit burst 12000 limit 500m mtu 100000</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">sudo tc qdisc add dev eth0 parent 1: netem delay 50ms limit 10000</span></div>
</div>
<div>
<br /></div>
<div>
You'll notice we deleted and then re-added the latency - this is just a limitation of how we chain these qdiscs together. But give it a shot - try an iperf between the two boxes with TCP, and magic - you can get 850Mb/s now!</div>
<div>
<br /></div>
<h2>
A sustainable fix</h2>
<div>
We're not going to add shapers everywhere just because we know that some parts of our network have bad bottlenecks. It's okay though - <a href="http://lwn.net/Articles/564978/">smart people are working on a fix</a>. By adding a knob to TCP where we explicitly say how fast we want it to go, we can make TCP pace itself, instead of emptying out its window onto the network, and then getting upset when it doesn't get magically taken care of. This is still experimental, but I'm keen to hear if anyone has had any luck with it - this is a very important step forward for TCP, and will become gradually more important as our networks get longer and fatter.</div>
Sam Russellhttp://www.blogger.com/profile/03431137767320819102noreply@blogger.com8tag:blogger.com,1999:blog-7532990877498512287.post-13784872587781159042013-07-15T13:37:00.004-07:002013-07-15T13:37:44.006-07:00Building a better internet exchange with OpenFlow<h2>
Internet exchanges</h2>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://files-cdn.formspring.me/photos/20120729/n501600b67d9a6.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="http://files-cdn.formspring.me/photos/20120729/n501600b67d9a6.jpg" width="262" /></a></div>
<div>
<br /></div>
<div>
The internet is a collection of cables and routers - cables join the routers together, and routers decide where your data should go. Internet exchanges perform a crucial job in the middle of this - they provide a way for different (often competing) providers to exchange internet traffic. An internet exchange is typically implemented using some number of switches that are federated together somehow so that every port can exchange traffic with every other port as fast as they need to.</div>
<div>
<br /></div>
<h2>
Problems with internet exchanges</h2>
<div>
Many internet exchanges are implemented as a layer-2 broadcast domain, and often include a route server to make it easier for participants to exchange routes with everyone over a single peering. Layer-2 broadcast domains need to be well policed though, because people can easily mess with other peoples' traffic, either accidentally, or maliciously.</div>
<div>
<br /></div>
<div>
Things that you need to watch out for on a layer-2 broadcast domain:</div>
<div>
<ul>
<li>Broadcast traffic - ARP requests are necessary, others probably aren't</li>
<li>MAC spoofing</li>
<li>ARP spoofing/proxy ARP</li>
<li>STP & other lovely protocols</li>
</ul>
<div>
Things that you need at an IPv4/IPv6 peering exchange</div>
</div>
<div>
<ul>
<li>Unicast IPv4 and IPv6 traffic</li>
<li>ARP requests (not necessarily broadcast)</li>
<li>Legitimate ARP replies</li>
</ul>
<h2>
Where's the button to turn this all on?</h2>
</div>
<div>
You'll often build an internet exchange with cheap and big switches, so you're not likely to have a huge amount of functionality to do clever stuff like this - and I've never seen a switch config before that lets me check the validity of ARP replies. To do this in software, however, is pretty easy.</div>
<h2>
Software Peering Exchange</h2>
<div>
Here's something I put together last night: <a href="https://github.com/samrussell/ryu/blob/master/ryu/app/spe.py">https://github.com/samrussell/ryu/blob/master/ryu/app/spe.py</a></div>
<div>
<br /></div>
<div>
It's an extension of my OF1.2 learning switch, with an extra table to handle ARP entries. Here's how the tables are laid out:</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0rEqOuOYCc9N_GNXR6o2V7Jp_7j2TED0yaHiu-1cSatbDAPOpxPwSEpl4a2YsmYZkk7tjrGGZgG1oG5GAy2Aoi3FpRSEeSRq-HHKbL2QLlrDv1YX0hA_MRLRlTLUwCGPrzJHNmSiMy1pW/s1600/SPE2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="289" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0rEqOuOYCc9N_GNXR6o2V7Jp_7j2TED0yaHiu-1cSatbDAPOpxPwSEpl4a2YsmYZkk7tjrGGZgG1oG5GAy2Aoi3FpRSEeSRq-HHKbL2QLlrDv1YX0hA_MRLRlTLUwCGPrzJHNmSiMy1pW/s320/SPE2.png" width="320" /></a></div>
<div>
<br /></div>
<div>
The controller takes some simple JSON config of which IP is expected on each port, and loads these into the switch. Table 0 forwards all ARP packets to table 1, and table 1 checks the packets - if they're ARP requests, then it forwards them out the appropriate port (so ARP requests are effectively unicast across the internet exchange), and for ARP replies, it only allows each port to reply for its own IP address. Any packets that don't get forwarded out a port get sent to the controller (including valid ARP replies before we learn any MAC addresses, but the controller deals with this smartly & learns the MAC address properly).</div>
<div>
<br /></div>
<div>
This is combined with the learning switch that I posted earlier in the week, with one alteration - our source-MAC matches also have to match the ethertype, and this can be constrained in the controller to limit it to IPv4 or IPV6 (or either or - this could be done on a per port basis if you wanted to). Packets with known source-MACs are send to table 2, and then forwarded out the right port if we've learned their MAC - otherwise, it goes to the controller for processing.</div>
<h2>
What else can we do?</h2>
<div>
We can lock down the controller a bit to only allow IPv4, IPv6 and ARP - this would stop CDP, STP and the like leaking into the exchange - only valid traffic, and no spoofing.</div>
<div>
<br /></div>
<div>
We could go a step further though - we could build in ACLs on each port based on the routes that they announce for basic RPF, so that a participant can only route traffic from an IP range if they advertise that route to the exchange already - this would prevent asymmetric routing.</div>
<div>
<br /></div>
<div>
So go, have a play - see if you can break it or find something else to add!</div>
<div>
<br /></div>
Sam Russellhttp://www.blogger.com/profile/03431137767320819102noreply@blogger.com1tag:blogger.com,1999:blog-7532990877498512287.post-24256156952694045272013-07-14T04:08:00.002-07:002013-07-14T04:08:46.351-07:00OpenFlow 1.2 switch app with multiple tables<h2>
Another OpenFlow switch app?</h2>
<div>
I'm sorry - there's quite a few of these, but this one is cool. I've been part of a couple of OpenFlow bootcamps in the last year and a bit, and an example that I've used is the pyswitch app that came stock with NOX. It's only a few lines of functional code, but it's a nice example to show what OpenFlow controller code looks like, and how you can easily build a learning switch from scratch. We'd go through a couple of scenarios on the whiteboard, but then we'd find a problem that OpenFlow 1.0 couldn't solve.</div>
<div>
<br /></div>
<h2>
Why we need multiple flow tables</h2>
<div>
Let's say we have two nodes connected to the OpenFlow switch. Node 1 sends a packet to node 2, and the switch doesn't know this MAC address, so it passes the packet up to the controller. The controller learns the MAC address of node 1 (where the packet came from), and assigns it to port 1. It then pushes a flow to the controller to make sure that traffic for node 1 goes out the right port, and then sends the packet on its way.</div>
<div>
<br /></div>
<div>
What happens when node 2 replies?</div>
<div>
<br /></div>
<div>
The switch gets the packet for node 1, has a flow for it, and forwards it. There's a problem here though - because the packet never went to the controller, the switch never learns the MAC address of node 2 - so all traffic to node 2 will go via the controller and clog things up.</div>
<div>
<br /></div>
<div>
The OpenFlow learning switch apps that I've seen deal with this by storing pairs - they match on a destination MAC address *and* on the source MAC address - this way, whenever we get a packet from a new MAC address, we make sure the controller knows and pushes out the right flow. This works perfectly, but it's inefficient - we need O(n^2) flows, meaning 100 MAC address on a switch requires 20,000 flows (one flow in each direction = 2 flows for each pair of mac addresses).</div>
<div>
<br /></div>
<div>
This is where later versions of OpenFlow start to shine. We can create multiple flow tables, so we have an initial table to check the source MAC (and forward to the controller if we don't know it), and a second table to check the destination MAC (and forward to controller, or just flood, if we don't know it). This way we need to record each source MAC once, and each destination MAC once - so 100 MAC addresses on a switch only needs 200 flows - only O(n).</div>
<div>
<br /></div>
<h2>
What it looks like</h2>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijN_twNYKkh_82VCBEYoQGpHNSDRJXx-TApiX6a3TKOSWv-ZItzwFfYEP9o7ujH6R14A6L-aP3QUu_jKhI-BtWWW4_7AN77boi8WVHhuRCreMzpim8ph-bUawgA8_yJt4KyuImoVYZWcr2/s1600/of12.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="88" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijN_twNYKkh_82VCBEYoQGpHNSDRJXx-TApiX6a3TKOSWv-ZItzwFfYEP9o7ujH6R14A6L-aP3QUu_jKhI-BtWWW4_7AN77boi8WVHhuRCreMzpim8ph-bUawgA8_yJt4KyuImoVYZWcr2/s320/of12.png" width="320" /></a></div>
<div>
<br /></div>
<div>
I've spent the whole weekend getting OpenVSwitch working so I could test it, but this is the final product. My good friend Josh suggested I use the Ryu controller as it has OpenFlow 1.2 and 1.3 support. It comes with a simple_switch application for OpenFlow 1.0, so I modified this to make it work with OpenFlow 1.2. There's a couple of subtle differences between the protocols, but once it was refactored for 1.2, it was pretty easy to set up a second table. The <a href="https://github.com/samrussell/ryu">code is here</a> if you want it, and you can just clone this repo when you want to use Ryu - it's up to date with the current github version (as of 14 July 2013), but I'm hoping they'll accept my pull request and just add my app into the main build.</div>
<div>
<br /></div>
<h2>
Next steps</h2>
<div>
I really want to get BIRD working with an OpenFlow controller, so we'll see if that happens - I've got BIRD pushing out a stream of JSON routes that anything can pick up, and I just need this to get turned into flows. It really does feel like we're getting closer to a production OpenFlow controller that plugs into a cheap switch and takes a whole internet route table. Now is a very good time to be a network engineer!</div>
Sam Russellhttp://www.blogger.com/profile/03431137767320819102noreply@blogger.com1tag:blogger.com,1999:blog-7532990877498512287.post-80063447151788010612013-07-14T03:38:00.003-07:002013-07-16T23:43:23.804-07:00How to build an OpenFlow testbed on an Ubuntu 12.04 VM in VirtualBox<h2>
Installing Ubuntu</h2>
I started with an Ubuntu Server 12.04.2 64 bit iso, and a VirtualBox VM with 1024MB of RAM and 8GB of hard disk. My version of VirtualBox is 4.0.10r72479 on Windows 7 x64 Professional. The install is pretty normal - if you've never installed Ubuntu Server before, you shouldn't find this too hard - just follow the prompts and keep pressing enter.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgoA-RgRSrwaWx0wbXH1afuc1nTMi5HPbozqbejux_jkLs_ybUhiXSg_S9NJFH9gvEkuopGNxBa1_n43R38eWtVU5hLI7rubhYA-xs20CDYZXu78AliilF7rU7OnVP5dDKHhH6BzuIOZ4mp/s1600/install1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="267" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgoA-RgRSrwaWx0wbXH1afuc1nTMi5HPbozqbejux_jkLs_ybUhiXSg_S9NJFH9gvEkuopGNxBa1_n43R38eWtVU5hLI7rubhYA-xs20CDYZXu78AliilF7rU7OnVP5dDKHhH6BzuIOZ4mp/s320/install1.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
This would be a good time to pour yourself a <strike>single malt</strike> coffee</div>
<br />
Don't be too fussy about packages, but as a rule I tend to want to install the OpenSSH server just because it's a good habit to get into - and something that'll trip you up if you forget to just that one time when it's really important.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjqLlGN3HeSRs721Z8EDwdtOm7E8L_FytOLd3y-oyMjWhv3eNcnv3aocNlP-R70iCYFlpuamXOqMUKA42FBR0aETxGEsS8JNMDhUrvHdKjS1kKsXcSuvedFPjo1TK7DJisu0lsyBqRj4CxY/s1600/install2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="267" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjqLlGN3HeSRs721Z8EDwdtOm7E8L_FytOLd3y-oyMjWhv3eNcnv3aocNlP-R70iCYFlpuamXOqMUKA42FBR0aETxGEsS8JNMDhUrvHdKjS1kKsXcSuvedFPjo1TK7DJisu0lsyBqRj4CxY/s320/install2.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
Pro tip ™</div>
<br />
This will take a couple of minutes to finish, and then you'll have an Ubuntu install ready to go. Remember to eject the ISO, and then turn the machine off. Time for the ugly stuff.<br />
<h2>
Configuring the network stuff</h2>
<div>
I've set up my testbed with 3x Ubuntu servers, 2 of which were set up like this and left, and a third that we did some special stuff with. For all of them, we'll need to set up extra adaptors on Internal Networks - the two client machines each get a single new adaptor with their own intnet, and the OVS machine (the third one) gets two new adaptors - the first one goes onto intnet1 (to connect to client 1), and the second goes onto intnet2. I've left the original adaptor untouched on all of the machines so we can add packages later without having to break networking.</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhmcGPXNVrooSDC0adqVGRioyYS5otgnAhyZt2S08OMd61kr6MhiuJUxdOBxQJGZd8c7Yh-p3mwdkpeO_7PdQjo6K1vw8PdpGfrt46_7OYdHVa_dxIzJoWpJ8KutSpUsAO5AR_VWOkzr-Ys/s1600/vms.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="227" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhmcGPXNVrooSDC0adqVGRioyYS5otgnAhyZt2S08OMd61kr6MhiuJUxdOBxQJGZd8c7Yh-p3mwdkpeO_7PdQjo6K1vw8PdpGfrt46_7OYdHVa_dxIzJoWpJ8KutSpUsAO5AR_VWOkzr-Ys/s320/vms.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
Edit our new OFSwitch2 VM</div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEihTSm5n14m7beyzOCFRu4myXzaCZ__5BdRZd14COXWVXEerOROk_CeP6Ix_ST1CFOJKYuLWaT9XgRn9FbQxy1wHTKQSzochQp4gCK7nRHAbCCcuObXOXuhniQjVGT-oqCovgb67WctbIea/s1600/vms2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="246" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEihTSm5n14m7beyzOCFRu4myXzaCZ__5BdRZd14COXWVXEerOROk_CeP6Ix_ST1CFOJKYuLWaT9XgRn9FbQxy1wHTKQSzochQp4gCK7nRHAbCCcuObXOXuhniQjVGT-oqCovgb67WctbIea/s320/vms2.png" width="320" /></a></div>
<div class="" style="clear: both; text-align: center;">
Keep Adapter 1 as is so we can download stuff</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiV5e3n_E-Bh9-3XSor0idZOkBtFfgv3zNHDHNbhmVnc1joEE9S0IcD2hxonrhduoIntWW6IVMXH5XQjS0EaL3dRhSlcUi1NT1wjtIU5tGOcz0E7TIAcBszWooIvqV4iamnOHe5lZxq927g/s1600/vms3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="245" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiV5e3n_E-Bh9-3XSor0idZOkBtFfgv3zNHDHNbhmVnc1joEE9S0IcD2hxonrhduoIntWW6IVMXH5XQjS0EaL3dRhSlcUi1NT1wjtIU5tGOcz0E7TIAcBszWooIvqV4iamnOHe5lZxq927g/s320/vms3.png" width="320" /></a></div>
<div class="" style="clear: both; text-align: center;">
Intnet1 matches up with client VM 1</div>
<div class="" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiralgd06FJFaYbfBR9toevDg4wxax2FS0sRvFGCnbEUmcndYQNeRHj9QrZt9tj8j_7aisV4H6WNbyR7w8OskP08lrrpFt8FV2AR0YBnJYgtdNtqFPjX-mib8rmNbSZ14IT5mRamX1hvq9k/s1600/vms4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="246" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiralgd06FJFaYbfBR9toevDg4wxax2FS0sRvFGCnbEUmcndYQNeRHj9QrZt9tj8j_7aisV4H6WNbyR7w8OskP08lrrpFt8FV2AR0YBnJYgtdNtqFPjX-mib8rmNbSZ14IT5mRamX1hvq9k/s320/vms4.png" width="320" /></a></div>
<div class="" style="clear: both; text-align: center;">
Intnet2 to client VM 2</div>
<br />
Once you've set this up, find the vbox file for your VM and open it up in your text editor, Make sure you <b>close VirtualBox first</b> - otherwise it won't take your changes. You'll want to add the following lines:<br />
<br />
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;"><ExtraDataItem name="VBoxInternal/Devices/e1000/1/LUN#0/Config/IfPolicyPromisc" value="allow-all"/></span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;"><ExtraDataItem name="VBoxInternal/Devices/e1000/2/LUN#0/Config/IfPolicyPromisc" value="allow-all"/></span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkeTR3LM98xcgkwLPFrK2SNr5BRuSWIP6nLhZhIvfrtLxb8I76qkUO9jAd4YwCgRagLIUSKRT6YgdPhEDQ1_AUZIHhk4rFEcwNVDcoXnpOs4QoiYI_icrO9E9vUDsK-biGivC-x5qXeMDB/s1600/vms5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="49" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkeTR3LM98xcgkwLPFrK2SNr5BRuSWIP6nLhZhIvfrtLxb8I76qkUO9jAd4YwCgRagLIUSKRT6YgdPhEDQ1_AUZIHhk4rFEcwNVDcoXnpOs4QoiYI_icrO9E9vUDsK-biGivC-x5qXeMDB/s320/vms5.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
The secret sauce</div>
<br />
That last part is super important - I spend a few hours today and last night trying to figure out why some packets would hit the bridge and others wouldn't - VirtualBox by default will accept broadcasts and unicasts to your address, but not other MAC addresses. Being a switch, you generally want to accept every MAC address except your own, so this is fairly important.<br />
<h2>
Installing OpenVSwitch</h2>
I've used version 1.10 because it's the coolest. Download it to a folder on your VM, untar, and read the INSTALL file because that's what cool kids do. In actual fact, there's a INSTALL.Debian, but that didn't work for me, so I just built it the generic way.<br />
<br />
Packages to install (so you don't spend the next hour chasing dependencies):<br />
<br />
<ul>
<li>build-essential</li>
<li>pkg-config</li>
<li>autoconf</li>
<li>automake</li>
<li>python-qt-dev</li>
<li>python-dev</li>
<li>python-twisted-conch</li>
<li>libtool</li>
</ul>
<div>
Then run the install</div>
<div>
./boot.sh</div>
<div>
./configure</div>
<div>
make</div>
<div>
sudo make install</div>
<div>
<br /></div>
<div>
I'm pleasantly surprised to say that this all worked the first time - just make sure you install all of those packages in one go and it'll work perfectly from the start :)</div>
<div>
<br /></div>
<h2>
Running OpenVSwitch</h2>
<div>
Now is a good time to start up OpenVSwitch to test that everything is working as you should expect - if we do this right, then the OpenFlow part will be easy. Fire up your two client machines, and set up eth1 on both of them to IPs in the same range - I've used 10.1.1.1/24 and 10.1.1.2/24, but use something else if this would clash with your other network.</div>
<div>
<br /></div>
<div>
Once you have them up, start up OpenVSwitch with the following stuff - I've kept them in separate screens to make it easier</div>
<div>
<br /></div>
<div>
Start a screen (screen)</div>
<div>
<br /></div>
<div>
Screen 0:</div>
<div>
sudo modprobe openvswitch</div>
<div>
<span class="il">sudo ovsdb</span>-tool create</div>
<br />
<div>
<span class="il">sudo ovsdb</span>-<span class="il">server</span> --remote=ptcp:9999:127.0.0.1</div>
<div>
<br /></div>
<div>
New screen(CTRL+A, C)</div>
<div>
<br /></div>
<div>
Screen 1</div>
<div>
<div>
sudo ovs-vswitchd tcp:127.0.0.1:9999</div>
<div>
<br /></div>
<div>
Screen 2</div>
<div>
ovs-vsctl --db=tcp:127.0.0.1:9999 add-br br0</div>
</div>
ovs-vsctl --db=tcp:127.0.0.1:9999 add-port br0 eth1<br />
<div>
ovs-vsctl --db=tcp:127.0.0.1:9999 add-port br0 eth2<br />
ovs-vsctl --db=tcp:127.0.0.1:9999 set bridge br0 protocols=OpenFlow12</div>
<div>
sudo ifconfig eth1 up</div>
<div>
sudo ifconfig eth2 up</div>
<div>
<br /></div>
<div>
If you bring up your client VMs you should be able to ping between them now. If you can, then great - we'll move onto getting OpenFlow working. You need one more line of code, assuming the controller is (or will be) on the same machine:</div>
<div>
<br /></div>
<div>
<div>
ovs-vsctl --db=tcp:127.0.0.1:9999 set-controller br0 tcp:127.0.0.1:6633</div>
</div>
<h2>
Getting OpenFlow going</h2>
<div>
We're on the home straight here. You can install the controller of your choosing, or you can install Ryu with the following instructions:</div>
<div>
<br /></div>
<div>
sudo apt-get install git python-setuptools</div>
<div>
git clone http://github.com/osrg/ryu</div>
cd ryu<br />
<div>
<div>
sudo python setup.py install</div>
</div>
<div>
<br /></div>
<div>
You can then sit and watch as it downloads its dependencies from pypi. When it's done, fire up the controller with an app, and you're ready to go.</div>
<div>
<br /></div>
<div>
ryu-manager ryu/app/simple_switch.py</div>
<div>
<br /></div>
<div>
You can check the flow tables (in another screen) with the following command:</div>
<div>
<br /></div>
<div>
sudo ovs-ofctl dump-flows br0</div>
<div>
<br /></div>
<div>
Check out the manpages if you want to learn more:</div>
<div>
<br /></div>
<div>
<a href="http://openvswitch.org/cgi-bin/ovsman.cgi?page=utilities%2Fovs-ofctl.8">http://openvswitch.org/cgi-bin/ovsman.cgi?page=utilities%2Fovs-ofctl.8</a></div>
<div>
<a href="http://openvswitch.org/cgi-bin/ovsman.cgi?page=utilities%2Fovs-vsctl.8">http://openvswitch.org/cgi-bin/ovsman.cgi?page=utilities%2Fovs-vsctl.8</a><br />
<br />
<h2>
What next?</h2>
</div>
<div>
You've got an OpenFlow testbed now, you can do what you want with it. Play with different controllers, or different versions of OpenFlow - it's all up to you.</div>
Sam Russellhttp://www.blogger.com/profile/03431137767320819102noreply@blogger.com0tag:blogger.com,1999:blog-7532990877498512287.post-18106321949636957192013-07-14T01:31:00.000-07:002013-07-14T01:31:00.539-07:00OpenFlow 1.2 with Ryu PREVIEWI've been playing with the Ryu OpenFlow controller this weekend, and I've got something that's nearly ready for you - here's a sneak preview<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJ0Gkh3xWRlRb2Koj6oi-2f-Mnd26YTyqPYG5DtgeRFkDor1PD43_XFDUZCXQoHpzEO3ynOZVDTEXPhQZLjGCVud58JXNO49QI_E82P6wpn2NuL2Iony5twv22f-EWYh_7PiGt4CK4rUT8/s1600/ryu.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="51" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJ0Gkh3xWRlRb2Koj6oi-2f-Mnd26YTyqPYG5DtgeRFkDor1PD43_XFDUZCXQoHpzEO3ynOZVDTEXPhQZLjGCVud58JXNO49QI_E82P6wpn2NuL2Iony5twv22f-EWYh_7PiGt4CK4rUT8/s400/ryu.png" width="400" /></a></div>
<br />
<a href="https://github.com/samrussell/ryu/blob/master/ryu/app/simple_switch_12.py">https://github.com/samrussell/ryu/blob/master/ryu/app/simple_switch_12.py</a>Sam Russellhttp://www.blogger.com/profile/03431137767320819102noreply@blogger.com0tag:blogger.com,1999:blog-7532990877498512287.post-50342576862427287412013-07-02T22:06:00.002-07:002013-07-02T22:06:41.190-07:00SDN plugin for the BIRD software router<h2>
A BGP router for SDN and OpenFlow</h2>
I've been playing with the BIRD software router for a couple of weeks to make it pipe out routes that I can play with. The reason for this is so that we can leverage the years of development time spent on the BGP side of things, and then simply translate the routes into flows to make a BGP router. There are already projects using RouteFlow for this, but we can simplify things a lot further - RouteFlow relies on VMs that run Quagga instances, and I feel it would be much better if the software router just talked directly to the OpenFlow controller.<br />
<h2>
What does it look like?</h2>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrnvE1_sZdV5td1fcN6AMi3PPlqukaHhctuSYrGtuUicQaxyxAxc1IKukgyIxt96qwNVnrSnlanRl5nvCyjBfQ1MFRxrR8RvSqml9goVOHhYuG8JPlyA_Qko5S4m_msu5ImRGL-yLwYkWg/s640/Screen+Shot+2013-07-03+at+4.41.40+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="185" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrnvE1_sZdV5td1fcN6AMi3PPlqukaHhctuSYrGtuUicQaxyxAxc1IKukgyIxt96qwNVnrSnlanRl5nvCyjBfQ1MFRxrR8RvSqml9goVOHhYuG8JPlyA_Qko5S4m_msu5ImRGL-yLwYkWg/s400/Screen+Shot+2013-07-03+at+4.41.40+PM.png" width="400" /> </a></div>
<div class="separator" style="clear: both; text-align: left;">
Every time a route update comes through to BIRD, BIRD pipes it out to a file. We can then get SDN controllers to follow this file and pull out the routes in JSON format and process as they wish to.</div>
<h2 class="separator" style="clear: both; text-align: left;">
What's next?</h2>
Make an OpenFlow controller that polls the file and pushes out flows to your OF switch(es). This stuff isn't rocket science - just download and install and see it work for yourself!<br />
<br />
<a href="https://github.com/samrussell/bird/tree/sam">https://github.com/samrussell/bird/tree/sam</a>Sam Russellhttp://www.blogger.com/profile/03431137767320819102noreply@blogger.com3tag:blogger.com,1999:blog-7532990877498512287.post-8483327361083568222013-06-02T01:45:00.002-07:002013-06-04T20:36:56.988-07:00Brocade and Juniper Interop - OSPF, MPLS, VLL/VPLS, and VRF interconnects<h2>
From the start</h2>
<div>
We run a mix of Brocade MLX and Juniper MX80's at work, and I've spent the last week trying to make them talk to each other properly. You'd think that by 2013, a multi-vendor network would work fine using standardised protocols, but it's still quite time consuming finding which ways work and which ways most certainly don't. Oddly enough, I'm not the only person who's been working on this recently - <a href="http://www.forwardingplane.net/2013/05/mpls-pseudowire-vll-between-junos-and-brocade-mlxxmr/">Nick Buraglio has done a bit in the last couple of weeks too</a> (thanks for the help on this)</div>
<div>
<br /></div>
<div>
As SDN starts to take over, this will become much less of a problem, but until then, here's how to do the following with Juniper MX-series routers and Brocade MLX/XMR chassis:</div>
<div>
<ul>
<li>Jumbo frames</li>
<li>OSPF</li>
<li>MPLS</li>
<li>VPLS</li>
<li>VLL/l2circuit</li>
<li>Tagging VPLS/VLL/l2circuit into a VRF on Juniper</li>
</ul>
<div>
Disclaimer - the MX80 chassis has lots of stuff built in, including a tunnel services PIC - we need this for some of the stuff below, not sure how it works with bigger chassis.</div>
</div>
<div>
<br /></div>
<h2>
Jumbo frames</h2>
<div>
This should be easy, but there's a couple of things that will trip you up if you aren't careful. The maximum frame size you can have on the Brocades is 9216 bytes, and on the Junipers it's 9192 bytes. I tried to set the frame size on the Brocades down to 9192 bytes and found a weird quirk - I could send 9146 byte pings from the Juniper, but the Brocade would only respond to 9142 byte pings - it appears the Brocades include the FCS in their count of frame size.</div>
<div>
<br /></div>
<div>
In the end, the best solution was to just leave both routers at their maximum values, set VPLS/VLL MTUs to 9100 bytes (or some number with a bit of headroom over 9000), and IP MTUs to 9000 (except for OSPF interfaces, but we'll get to that soon).</div>
<div>
<br /></div>
<div>
The main thing to remember is that on the Junipers, you can set MTU on the physical interface, or inside a "family XXX" stanza, but not directly on a logical interface. If you want to set the IP MTU for a logical interface, it sits in "interface XXX -> unit Y -> family inet -> mtu 9000"</div>
<div>
<br /></div>
<h2>
OSPF</h2>
<div>
You *can* stand up OSPF with Jumbo frames, but it's fine with 1500 byte frames. We're in the position of introducing Junipers into our Brocade OSPF cloud, and since the defaults for Brocade are already 1500 bytes, it's easier to step the Junipers down than bring the Brocades up. I've set up the Brocade-facing interface like this:</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">interfaces {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> ge-1/0/0 {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> vlan-tagging;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> mtu 9192;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> unit 1000 {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> vlan-id 1000;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> family inet {</span></div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> mtu 1500</span><br />
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> address 10.1.2.1/30;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> }</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> family mpls;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> }</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> }</span></div>
</div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">}</span></div>
<div>
<br /></div>
<div>
The Brocade end looks like this:</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">interface ve 100</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> bfd interval 100 min-rx 100 multiplier 3</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> ip ospf area 0</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> ip ospf cost 100</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> ip ospf dead-interval 40</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> ip ospf hello-interval 10</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> ip address 10.2.3.2/30</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> ip mtu 1500</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">!</span></div>
</div>
<div>
<br /></div>
<div>
The only tricky bit is making sure the IP MTU is the same for each end - if you get a huge route update going into an interface that can't take the whole packet then you'll end up blackholing routes. Junipers are supposed to not stand up OSPF when there's an IP MTU mismatch, but it doesn't always work - it pays to test with ping packets to confirm - you should be able to ping in either direction with a 1472 bytes of data (1500 - 20 byte IP header - 8 byte ICMP header).</div>
<div>
<br /></div>
<h2>
MPLS</h2>
<div>
This is pretty straightforward - set up loopback interfaces on each end, and enable RSVP and LDP. We'll use RSVP for the outer tags, and LDP for the inner tags - no clever BGP signalling here.</div>
<div>
<br /></div>
<div>
Juniper:</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">protocols {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> rsvp {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> interface all {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> disable;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> }</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> interface ge-1/0/0.1000;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> interface lo0.0;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> }</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> mpls { </span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> label-switched-path 1-to-2 {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> from 10.0.0.1;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> to 10.0.0.2;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> fast-reroute;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> }</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> label-switched-path 1-to-3 {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> from 10.0.0.1;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> to 10.0.0.3;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> fast-reroute;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> }</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> interface all {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> disable;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> }</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> interface ge-1/0/0.1000;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> }</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> ospf {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> traffic-engineering;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> area 0.0.0.0 {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> interface all {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> disable;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> }</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> interface ge-1/0/0.1000 {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> hello-interval 3; </span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> dead-interval 12;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> bfd-liveness-detection {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> minimum-interval 300;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> multiplier 3;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> }</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> }</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> interface lo0.0 {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> passive;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> }</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> }</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> }</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> ldp {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> interface all {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> disable;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> }</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> interface lo0.0;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> }</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">}</span></div>
</div>
<div>
<br /></div>
<div>
Brocade:</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">router mpls</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> policy</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> traffic-eng ospf</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> mpls-interface ve100</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> path 3-to-1</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> loose 10.0.0.1 </span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> path 3-to-2</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> loose 10.0.0.2</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> path S3-to-1</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> loose 10.0.0.1</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> path S3-to-2</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> loose 10.0.0.2</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> lsp LSP-3-to-1</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> to 10.0.0.1</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> primary 3-to-1</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> secondary S3-to-1</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> standby</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> frr</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> revert-timer 30</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> enable</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> lsp LSP-3-to-2</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> to 10.0.0.2</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> primary 3-to-2 </span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> secondary S3-to-2</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> standby</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> frr</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> revert-timer 30</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> enable</span></div>
</div>
<h2>
VPLS</h2>
<div>
This is where it gets interesting. In my opinion, Brocade does the right thing (packets come out of a VLAN-tagged "pipe", and then go into a VPLS "pipe"), whereas Juniper does it at a lower and less-abstract level (packets with headers that get altered) - it's more flexible, but it's harder to make it do the right thing.</div>
<div>
<br /></div>
<h3>
Raw mode, tagged mode, and tags inside raw mode</h3>
<div>
VPLS has two modes - Raw mode creates a broadcast domain between all peers on the same VPLS, whereas tagged mode allows you to use inner tags within a VPLS circuit. The problem comes where Juniper sends 802.1q VLAN-tagged packets through a raw-mode VPLS. You end up left with a situation where traffic can go one way, but not the other, and it's all quite confusing.</div>
<div>
<br /></div>
<h3>
Raw mode interop</h3>
<div>
Check out these configs:</div>
<div>
<br /></div>
<div>
On Juniper, we make a routing instance, and add interfaces into it. They can be VLAN-tagged or normal access ports, and there's a very important trick to make it all use raw mode properly - the line "vlan-id none". If you don't do this, packets on an untagged port go through fine, but packets from tagged ports come through with 802.1q VLAN tags on them. On Brocade, a VPLS get delivered to a mix of tagged and untagged ports, but all traffic is sent as normal untagged ethernet. The "vlan-id none" line makes the Junipers behave in the same way. The config below delivers VPLS 40 untagged at both ends, and VPLS 140 tagged as VLAN 140.</div>
<div>
<br /></div>
<div>
Don't worry too much about the MTUs - they need to match up, but they don't appear to be enforced. We picked 9100 as it's well under the 9192 byte hardware MTU, but well above the 9000 byte IP MTU - a bit of leeway in each direction.</div>
<div>
<br /></div>
<div>
Juniper:</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">routing-instances {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> vpls-40 {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> description vpls-40;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> instance-type vpls;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> vlan-id none;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> interface ge-1/1/9.40; </span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> protocols {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> vpls {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> no-tunnel-services;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> vpls-id 40;</span></div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> mtu 9100;</span><br />
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> neighbor 10.0.0.2;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> neighbor 10.0.0.3;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> }</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> }</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> }</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> vpls-140 {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> description vpls-140;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> instance-type vpls;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> vlan-id none;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> interface ge-1/1/9.140; </span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> protocols {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> vpls {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> no-tunnel-services;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> vpls-id 140;</span></div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> mtu 9100;</span><br />
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> neighbor 10.0.0.2;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> neighbor 10.0.0.3;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> }</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> }</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> }</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">}</span></div>
</div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">interfaces {</span></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> ge-1/1/9 {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> flexible-vlan-tagging;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> native-vlan-id 40;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> mtu 9192;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> encapsulation flexible-ethernet-services;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> unit 40 {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> encapsulation vlan-vpls;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> vlan-id 40;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> family vpls;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> }</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> unit 140 {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> encapsulation vlan-vpls;</span></div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> vlan-id 140;</span><br />
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> family vpls;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> }</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> }</span></div>
</div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">}</span></div>
<div>
<br /></div>
<div>
Brocade:</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">router mpls</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> vpls vlan40 40 </span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> vpls-peer 10.0.0.1 10.0.0.2</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> vpls-mtu 9100</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> vlan 40</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> untagged ethe 1/5 </span></div>
</div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><br /></span></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> vpls vlan40 40 </span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> vpls-peer 10.0.0.1 10.0.0.2</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> vpls-mtu 9100</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> vlan 140</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> tagged ethe 1/5 </span></div>
</div>
<div>
<br /></div>
<div>
If you're interested in the mechanics behind the Juniper implementation, the "show interface" command gives you a bit of insight - Juniper interprets the "vlan-id none" line in the routing instance and converts that to tag push/pop operations on the interface:</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> Logical interface ge-1/1/9.40 (Index 332) (SNMP ifIndex 564) </span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> Flags: SNMP-Traps 0x0</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> VLAN-Tag [ 0x8100.40 ] Native-vlan-id: 40 In(pop) Out(push 0x8100.40) </span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> Encapsulation: VLAN-VPLS</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> Input packets : 9 </span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> Output packets: 7</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> Protocol vpls, MTU: 9192 </span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> Flags: Is-Primary</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> Logical interface ge-1/1/9.140 (Index 333) (SNMP ifIndex 597) </span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> Flags: SNMP-Traps 0x0 VLAN-Tag [ 0x8100.140 ] In(pop) Out(push 0x8100.140) </span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> Encapsulation: VLAN-VPLS</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> Input packets : 149 </span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> Output packets: 92</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> Protocol vpls, MTU: 9192</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> Flags: Is-Primary</span></div>
</div>
<div>
<br /></div>
<h2>
VLL/l2circuit</h2>
<div>
This is the fun one. Tagged-mode VLLs are the easiest to get up and running, but raw-mode should be doable too. There is one problem though - the only way I can make raw-mode work on the Junipers looks like a filthy hack, but it produces the same results that we see above in the "show interface" output for VPLS.</div>
<div>
<br /></div>
<div>
First off, configs for tagged mode</div>
<div>
<br /></div>
<div>
Brocade:</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">router mpls</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> vll vlan42 42</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> vll-mtu 9100</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> vll-peer 10.0.0.1</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> vlan 42</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> tagged e 1/5</span></div>
</div>
<div>
<br /></div>
<div>
Juniper:</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">interfaces {</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: Courier New, Courier, monospace; font-size: x-small;">ge-1/1/9 {</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> flexible-vlan-tagging;</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> mtu 9192;</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> encapsulation flexible-ethernet-services;</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> unit 42 {</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> encapsulation vlan-ccc;</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> vlan-id 42; </span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> family ccc;</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> }</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: Courier New, Courier, monospace; font-size: x-small;">}</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">}</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">protocols {</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: Courier New, Courier, monospace; font-size: x-small;">l2circuit {</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> neighbor 10.0.0.3 {</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> interface ge-1/1/9.42 {</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> virtual-circuit-id 42;</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> mtu 9100;</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> encapsulation-type ethernet-vlan;</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> }</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> }</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: Courier New, Courier, monospace; font-size: x-small;">}</span></div>
</div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">}</span></div>
<div>
<br /></div>
<div>
This is all pretty straightforward, and works out of the box. Here's what the raw mode config looks like</div>
<div>
<br /></div>
<div>
Brocade:</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">router mpls</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> vll vlan41 41 raw-mode</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> vll-mtu 9100</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> vll-peer 10.0.0.1</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> vlan 41 </span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> tagged e 1/5</span></div>
</div>
<div>
<br /></div>
<div>
Juniper:</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">interfaces {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> ge-1/1/9 {</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> flexible-vlan-tagging;</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> mtu 9192;</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> encapsulation flexible-ethernet-services;</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> unit 41 {</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> encapsulation vlan-ccc;</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> vlan-id 41;</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> input-vlan-map pop;</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> output-vlan-map push;</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> family ccc;</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> }</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: Courier New, Courier, monospace; font-size: x-small;">}</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">}</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">protocols {</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: Courier New, Courier, monospace; font-size: x-small;">l2circuit {</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> neighbor 10.0.0.3 {</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> interface ge-1/1/9.41 {</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> virtual-circuit-id 41;</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> mtu 9100;</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> encapsulation-type ethernet;</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> }</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> }</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: Courier New, Courier, monospace; font-size: x-small;">}</span></div>
</div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">}</span></div>
<div>
<br /></div>
<div>
As you can see, the default for Brocade is tagged mode, so we need to explicitly put it in raw mode. On the Juniper end, we set the encapsulation type on the VLL to ethernet instead of ethernet-vlan, but this only works with encapsulation ethernet-ccc on the physical interface. If we have VLAN tagging mode enabled, there doesn't seem to be any way to tell the MX80 about this. The way I've made this work is with the "input-vlan-map" and "output-vlan-map" statements - they seem to round everything out and make it all work. Given the default for both Juniper and Brocade is tagged mode, and we need a bit of mad hax to make raw mod work here, it might make sense to use tagged mode.</div>
<div>
<br /></div>
<h2>
Tagging circuits to VRFs (Juniper only)</h2>
<div>
This was my favourite part. The way to do this seems to be lt- devices, which means you need to set up the tunnel services PIC (if you have one - the MX80's have one built in).</div>
<div>
<br /></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">chassis {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> fpc 0 {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> pic 0 {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> tunnel-services {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> bandwidth 1g;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> }</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> }</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> }</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">}</span></div>
<div>
<br /></div>
<div>
This next part took a while to figure out, but it totally works - you just need to make sure you match up the encapsulations and it all works fine.</div>
<div>
<br /></div>
<div>
Here's some config:</div>
<div>
<br /></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">routing-instances {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> vrf {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> instance-type vrf;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;">interface lt-0/0/10.1;</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> route-distinguisher 1:2;</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> vrf-target target:1:2;</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> vrf-table-label;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> }</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> vrf-43 {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> instance-type vrf;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;">interface lt-0/0/10.3;</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> route-distinguisher 1:42;</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> vrf-target target:1:42;</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> vrf-table-label;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> }</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> vpls-40 {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> description vpls-40;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> instance-type vpls;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> vlan-id none;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> interface lt-0/0/10.2; </span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> protocols {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> vpls {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> no-tunnel-services;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> vpls-id 40;</span></div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> mtu 9100;</span><br />
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> neighbor 10.0.0.2;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> neighbor 10.0.0.3;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> }</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> }</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> }</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">}</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">protcols {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> l2circuit {</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> neighbor 10.0.0.2 {</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> interface lt-0/0/10.4 {</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> virtual-circuit-id 43;</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> mtu 9100;</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> encapsulation ethernet-vlan;</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> }</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> }</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> }</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">}</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">interfaces {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> lt-0/0/10 {</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> mtu 9192;</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> unit 1 {</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> encapsulation ethernet;</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> peer-unit 2;</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> family inet {</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> mtu 9000;</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> address 192.168.0.13/24;</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> }</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> }</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> unit 2 {</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> encapsulation ethernet-vpls;</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> peer-unit 1;</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> }</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> unit 3 {</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> encapsulation ethernet;</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> peer-unit 4;</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> family inet {</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> mtu 9000;</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> address 192.168.43.13/24;</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> }</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> }</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> unit 4 {</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> encapsulation ethernet-ccc;</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> peer-unit 3;</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> }</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> }</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">}</span></div>
<div>
<br /></div>
<div>
This took about a day to get working, but it's totally simple once it matches up. The lt- devices are instead of crossover cables hanging out the back of your router, and the key is to just set the encapsulation types correctly. You can cheat a little with irb interfaces in VPLS routing instances, but this alters the route table directly on the chassis. Doing it this way means it's locked down to a VRF, and everything is a bit nicer.</div>
<h2>
End</h2>
<div>
I hope you've enjoyed this - let me know if there's anything I've missed or got wrong. The moral of the story here is - Juniper and Brocade can do VPLS and VLLs fine between each other - just watch out for the little quirks that would trip you up.</div>
<div>
<br /></div>
Sam Russellhttp://www.blogger.com/profile/03431137767320819102noreply@blogger.com4tag:blogger.com,1999:blog-7532990877498512287.post-9565565420377310372013-02-18T15:41:00.002-08:002013-02-18T15:41:33.817-08:00SamShares - Parsing financial data out of annual report PDFs<h2>
What's up</h2>
<div>
I've been doing a lot of financial research, and a big chunk of that is looking through financial reports, manually copying the fields for assets, liabilities, equity, EBIT etc. It's boring as hell, and takes a long time. Why can't we automate this?</div>
<div>
<br /></div>
<h3>
Parsing PDFs</h3>
<div>
I started by forking <a href="https://github.com/samrussell/PyPDF2">PyPDF2</a> to give me better access to the underlying objects. It's a fairly good start for working with PDFs, but just blurts out (some of) the text in a random order, which isn't what I want. This lead me down a bit of a rabbit hole and lead to me downloading a copy of the <a href="http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/pdf/pdfs/pdf_reference_1-7.pdf">PDF 1.7 reference</a> and browsing through this, sections 5.2 and 5.3 in particular</div>
<h3>
What's the plan?</h3>
<div>
<ul>
<li>Find the pages with assets/liabilites and income</li>
<li>Render them such that it's obvious where the columns and rows line up</li>
<li>Convert this to a spreadsheet</li>
<li>???</li>
<li>PROFIT</li>
</ul>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiDZ1UJCo1EW1Zvzkw8bcW5UqgQTkewRMNJUjd2QM_DvEr0wr5K1zK2a0E8LyqD8BvSuUYfidqUzX9wGWpJkMpeL7EKGQnSJtPWuPGd625nkKp9_ERnbGsxCWYeWuayrnLwX8sbj5e5qXQf/s1600/Screen+Shot+2013-02-19+at+12.29.20+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiDZ1UJCo1EW1Zvzkw8bcW5UqgQTkewRMNJUjd2QM_DvEr0wr5K1zK2a0E8LyqD8BvSuUYfidqUzX9wGWpJkMpeL7EKGQnSJtPWuPGd625nkKp9_ERnbGsxCWYeWuayrnLwX8sbj5e5qXQf/s320/Screen+Shot+2013-02-19+at+12.29.20+PM.png" width="305" /></a></div>
<div>
For example, above is a screenshot from the annual report of New Zealand's largest NZX company, Fletcher Building. The PDF displays like lovely rows and columns, but can't be easily accessed in this way. If we can parse the PDF and render all the text in place, we can then make fairly accurate guesses at which rows and columns the values fall into.</div>
</div>
<h3>
Quick primer to text in PDF</h3>
<div>
Here are some of the operators you'll find for manipulating text in a PDF</div>
<div>
<br /></div>
<div>
BT, ET - Start and end a text object. This initialises the text matrix to the identify matrix - i.e. positioned at the top left of the document</div>
<div>
Td, TD, T* - Operators to move the cursor to the next line</div>
<div>
TM - Sets the text matrix. This is an affine transform, with 6 parameters - the first 4 matter for manipulating the text itself (scaling, warping, italics), and the last two essentially just set the start point for the text. This is enough for us to cheat and guess which way the text will go</div>
<div>
Tc, Tw, Tf and lots more - Spacing and font settings</div>
<div>
<br /></div>
<div>
Tj, TJ - Display a text string - Tj does this simply, TJ has options after each character/substring for spacing information</div>
<div>
<br /></div>
<h3>
Putting it all together</h3>
<div>
To parse a table out of a PDF, here's the rough idea:</div>
<div>
<ol>
<li>Locate all the strings on a page (BT/ET and TJ/Tj operators)</li>
<li>Create a structure which ties the strings to locations (probably just Tm)</li>
<li>Assign values row and column IDs</li>
</ol>
<div>
Once this is done, just check what is at the leftmost and topmost of each table, and use these as keys to the data. For the above image, the field "total assets" lined up with "June 2012" gives two results, so these just need to be referenced to the headers at the top, OR we can cheat and use the leftmost as this is generally the convention.</div>
</div>
<h3>
Next steps</h3>
<div>
Assuming I can make all this work, the data will then just be stored in a DB of some sort, keyed by year and company. Once this is automated enough to just pull PDFs out of NZX announcements, it'll be left in the background accumulating data, eventually building a corpus of financial data from NZX companies that can be used to make financial analysis much, much quicker and more versatile than it currently is.</div>
<div>
<br /></div>
<div>
<br /></div>
Sam Russellhttp://www.blogger.com/profile/03431137767320819102noreply@blogger.com3tag:blogger.com,1999:blog-7532990877498512287.post-35428153421595694912013-02-12T14:58:00.001-08:002013-02-12T15:28:30.155-08:00OpenFlow 1.0 support on Juniper MX240 with JunOS 12.3<h2>
Juniper have added OpenFlow to JunOS 12.3</h2>
<div>
Do you have a spare MX240 lying around? Chuck a copy of JunOS 12.3 on it and you can get Openflow 1.0 up and running and have a play.<br />
<br />
<h3>
Details</h3>
</div>
<div>
<a href="https://www.dropbox.com/sh/hpzhwwjyzf4pq09/y8Vk3PjBjU">I had a skim over the documentation (download from DropBox)</a>, and a few things jumped out at me:</div>
<div>
<ul>
<li>Fairly full OF1.0 implementation. I don't have a spare MX240 to test, but it would appear that everything is handled in hardware (not sure how Junipers could do otherwise tbh)</li>
<li>Supports multiple VLANs - if these can be turned on and off from the controller then this would be awesome (let me know if you find this out)</li>
<li>Doesn't handle buffered packets - make sure your controller can handle OFPT_PACKET_IN messages that don't send a buffer ID (current version of POX doesn't do this?, but the <a href="https://github.com/noxrepo/pox/tree/betta">betta branch</a> does)</li>
<li>Doesn't handle TLS connectivity to the controller - not the end of the world, but I'm curious as to why this was done</li>
<li>Doesn't do anything related to STP... who cares?</li>
<li>Only supports MX240s...</li>
</ul>
<div>
This looks like a great start, well done Juniper! Here's my list of requests for the next iteration:</div>
</div>
<div>
<ul>
<li>Support more than one device :) MX80's would be great, also looking to see what the EX series implementation looks like</li>
<li>Buffered packets! Everyone else does this, and it greatly speeds up the flows-per-second bottleneck between the switch and controller</li>
</ul>
<div>
That's pretty much all from me. OF1.1 support (or 1.3 as this is where everyone is going) would be awesome so we can drive MPLS, but other than that, this is fantastic news.<br />
<h3>
Update</h3>
</div>
</div>
<div>
It looks like it's not quite ready for RouteFlow - Joe Stringer pointed this out in the notes:</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;">• If the controller pushes a flow with a set source MAC address action, the router cannot</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;"> program the corresponding filter term. However, CLI show commands still display the</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;"> flow with the associated action, and the device sends an OFPET_FLOW_MOD_FAILED</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;"> error message with an OFPMFC_UNSUPPORTED code to the controller. [PR 838699]</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;">• If the controller pushes a flow with a set destination MAC address action, the router</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;"> cannot program the corresponding filter term. However, CLI show commands still</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;"> display the flow with the associated action, and the device sends an</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;"> OFPET_FLOW_MOD_FAILED error message with an OFPMFC_UNSUPPORTED code</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;">to the controller. [PR 838709]</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;">• If a flow contains a set IP source address action or a set IP destination address action,</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: xx-small;"> the device rejects the flow and sends an OFPET_FLOW_MOD_FAILED error m</span></div>
</div>
<div>
<br /></div>
<div>
In other words, no MAC/IP address rewrites = no routing :(</div>
<h4>
Disclaimer</h4>
<div>
I've been told that this info and the <a href="https://www.dropbox.com/sh/hpzhwwjyzf4pq09/y8Vk3PjBjU">linked documents</a> are public... If Juniper isn't happy with this, please get in touch and I'll fix it.</div>
Sam Russellhttp://www.blogger.com/profile/03431137767320819102noreply@blogger.com0tag:blogger.com,1999:blog-7532990877498512287.post-62205364467099791552013-01-25T02:27:00.000-08:002013-01-25T02:27:14.937-08:00Thimble - Secure, high-speed connectivity with OpenFlow & Science DMZ<h3>
I've been busy</h3>
<div>
Last week I had the pleasure of spending a week in Honolulu at TIP2013, going to workshops, watching presentations, and socialising with some of the most talented network engineers in the world, and I felt incredibly fortunate to do so. I also had the opportunity to present some of my OpenFlow work with them, and was amazed at the feedback that I got from everybody. The recording of my presentation is now <a href="http://events.internet2.edu/2013/tip/agenda.cfm?go=session&id=10002721&event=1261">available online</a>. I gave the same presentation this week at NZNOG, and I'll link to that when the on-demand video is available.</div>
<h3>
Thimble</h3>
<div>
For scientists to move big data in reasonable time frames, they need to have data transfer nodes outside of their campus firewalls, inside a <a href="http://fasterdata.es.net/science-dmz/">Science DMZ</a>. This is a proven way to optimise file transfers, but exposes your file transfer nodes to the whole internet. Thimble is a way of using OpenFlow to easily program ACLs into your edge switch, and I've covered it briefly <a href="http://pieknywidok.blogspot.co.nz/2012/08/djangoflow-web-ui-for-pox-openflow.html">here</a> and <a href="http://pieknywidok.blogspot.co.nz/2012/08/djangoflow-part-two-quick-and-simple-ui.html">here</a>. The variation that I presented at TIP2013 and NZNOG positions the Science DMZ between an edge router and campus firewall, allowing a subset of routes to be sent to the OpenFlow switch. This means we only need to send experimental traffic to the Thimble, allowing implementers to test this without risking production traffic.</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhpyajHTP15yD2TYk-KRfpTJhxmksAo-jbgQoUR_duzYZgxKf0keLuaXBJ2ShJXhtwp1Hl9oVQQEfJiN5N36WO5S9kmBn7ADeGyEnQa1GzIA8yy5-GEQDKjSvdpW6DvNKOyFlerpKT9YPg5/s1600/SDMZ.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="300" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhpyajHTP15yD2TYk-KRfpTJhxmksAo-jbgQoUR_duzYZgxKf0keLuaXBJ2ShJXhtwp1Hl9oVQQEfJiN5N36WO5S9kmBn7ADeGyEnQa1GzIA8yy5-GEQDKjSvdpW6DvNKOyFlerpKT9YPg5/s400/SDMZ.png" width="400" /></a></div>
<h3>
Clever stuff</h3>
<div>
You can federate a few Thimbles with a single controller, allowing a file transfer logged on the web application to trigger multiple switches around the world to be reconfigured. We're not just tied to a web interface either - there's nothing to stop us implementing uPnP at the edge and letting file transfer programs communicate with the network to arrange ACLs. The end goal is to take existing concepts and apply modern software design principles, allowing us to do things easily that used to be out of the question - network doesn't need to be this hard.</div>
<div>
<br /></div>
<div>
If you're interested in building a Thimble, I'd love to hear from you - leave a comment or email me and I'll be more than happy to hear your stories and help wherever I can.</div>
<h3>
RouteFlow in New Zealand</h3>
<div>
We've had some cool demos up and running this week - a distributed router was deployed at WIX and another data centre here in Wellington, and a RouteFlow deployment powered one of the internet feeds for the NZNOG conference this week. We had a screen up showing the flows present in the switch as people joined and left the wifi, and counters for data plane and control plane traffic. It was a nice visual demo - we could connect a cell phone, show the MAC and IP addresses in the new flow, and start a download to show the data plane traffic going up without changing the control plane traffic - just to prove that we don't need to send every packet via the controller!</div>
<h3>
What's coming in 2013</h3>
<div>
We're off to a really good start with OpenFlow this year, and I reckon the killer app is a year or two off at tops. Here's what I want to see happen:</div>
<div>
<ul>
<li>Distributed routers - a mesh of 5-10, all controlled centrally</li>
<li>MPLS!!! OpenVSwitch now does OpenFlow1.2, and the Pica8 switches have implemented this, so there's no excuse for us not having an OpenFlow replacement for LDP/RSVP</li>
<li>Breaking OSI. Flows can match on any combination of ethernet, IP, and TCP/UDP, so I want to see more clever stuff happening with that. OpenFlow will let you do longest-prefix-matching on MAC addresses if you want, or route based on TCP/UDP ports, or some other weird combination. You'd want to find a way to do this that would be valuable, but people just need to jump in and see what new ideas they can come up with.</li>
<li>???? - you tell me what you'd like to see in OpenFlow this year.</li>
</ul>
</div>
Sam Russellhttp://www.blogger.com/profile/03431137767320819102noreply@blogger.com1tag:blogger.com,1999:blog-7532990877498512287.post-73401353725370343212012-09-07T20:09:00.000-07:002012-09-07T20:10:11.574-07:00Tunneling traffic through your OpenFlow controller - Building a POX-based OpenFlow router<h2>
Why would you do this?</h2>
<div>
If we want to make an OpenFlow router, we need to be able to communicate with other non-OpenFlow routers. Normally, you would assign an IP address to your router, turn on BGP/OSPF, and then configure these protocols to talk to other routers using this IP address. With OpenFlow, the controller has the brains, but no obvious way to talk to other network devices. If only we could pretend that the controller was in the router somehow...</div>
<h2>
Can't we just look at the OpenFlow messages?</h2>
<div>
Sure, and we looked at this <a href="http://pieknywidok.blogspot.co.nz/2012/08/arp-and-ping-in-pox-building-pox-based.html">last week</a>, but it's clumsy and means we need to reinvent the wheel to make software routers talk to POX. RouteFlow abstracts this by loading software routers in virtual machines, last week's demonstration hardcodes everything into the controller, but tunnelling gives us a middle-of-the-road solution: no virtual machines needed, but we can still bind stuff to a network interface on the controller and let the linux network stack handle already-solved problems like TCP and the like.</div>
<h2>
Building a tunnel</h2>
<div>
Linux has a fantastic tool called TUN/TAP, which lets you create virtual network interfaces. One end talks to the Linux network stack and lets any application use it, and the other end talks to our program. In the spirit of keeping things modular, and minimising opportunities for me to write bad code, I've used the PyTap library to set this up. <a href="http://pypi.python.org/pypi/PyTap">PyTap</a> has a PIP package, which means we can easily add it to a virtualenv and continue to keep everything self-contained.</div>
<div>
<br /></div>
<div>
<i>Protip: TUN interfaces take IP packets, TAP interfaces take Ethernet packets</i></div>
<div>
<br /></div>
<div>
If you haven't used virtualenvs, here's the basic idea:</div>
<div>
<br /></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">virtualenv tundemo</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">cd tundemo</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">source bin/activate</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">pip install pytap</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">git clone http://github.com/noxrepo/pox</span></div>
<div>
<br /></div>
<div>
This will set you up with a virtualenv that has POX and PyTap ready to go. Despite being in a virtualenv, PyTap still needs root privileges, so you'll need to be root before source'ing into your virtualenv to make this work. If anyone can show me how to make this work without root privileges I'll be happy to hear (presumably some trickery with the /dev/net/tun device)</div>
<div>
<br /></div>
<div>
As with my other modules, I've hacked code into a copy of forwarding.l2_learning - this time I've renamed it to tundemo, and changed the name of the class all through the source.</div>
<div>
<br /></div>
<div>
Here are all my imports, add these at the top:</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace;">from pytun import TunTapDevice, IFF_TAP</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">from pox.lib.addresses import *</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">from pox.lib.packet import *</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">from threading import Thread</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">import subprocess</span></div>
</div>
<div>
<br /></div>
<div>
In the __init__() function, I've put the following code to make the TAP device:</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> # Our table</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> self.macToPort = {}</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> </span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> # TAP device</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> self.tap = TunTapDevice(flags=IFF_TAP)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> self.tap.addr = '10.1.1.13'</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> self.tap.netmask = '255.255.255.0'</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> self.tap.mtu=1300</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> print "hwaddr for " + self.tap.name + ": " + str(EthAddr(self.tap.hwaddr))</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> </span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> # Bring tap interface up</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> subprocess.check_call("ifconfig " + self.tap.name + " up", shell=True)</span></div>
</div>
<div>
<br /></div>
<div>
PyTap chooses a random MAC address when it creates the interface, so printing it out lets us debug things a bit easier.</div>
<div>
<br /></div>
<h3>
Tunneling fron TAP to switch</h3>
<div>
Once we have our TAP interface up, we need to handle packets that we receive on it. Let's set up a thread to handle this</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace;"># Create thread to read from tap and send to switch</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> self.th = Thread(target=handle_tap_in, args=(self))</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> self.th.daemon = True</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> self.th.start()</span></div>
</div>
<div>
<span style="font-family: Courier New, Courier, monospace;"></span><br />
<div>
<span style="font-family: Courier New, Courier, monospace;"> # Set max packet size to 1400 bytes</span></div>
<span style="font-family: Courier New, Courier, monospace;">
<div>
self.connection.send(of.ofp_set_config(miss_send_len=1400))</div>
</span></div>
<div>
<br /></div>
<div>
Our handler function is fairly straightforward</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace;">def handle_tap_in(switch):</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> while True:</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> packettap = switch.tap.read(switch.tap.mtu+24)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> print "Packet read from tap"</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> e = ethernet()</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> e.parse(packettap[4:])</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> </span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> port = of.OFPP_ALL</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> if e.dst in switch.macToPort:</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> port = switch.macToPort[e.dst]</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> </span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> msg = of.ofp_packet_out()</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> msg.data = packettap[4:]</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> msg.actions.append(of.ofp_action_output(port =</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> port))</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> switch.connection.send(msg)</span></div>
</div>
<div>
<br /></div>
<div>
This will send all packets that come up on the tap0 interface to the switch, and either floods them or sends them on the right port, depending on what MAC addresses we've already learned.</div>
<div>
<br /></div>
<h3>
Tunneling from switch to TAP</h3>
<div>
We already get sent packets from the switch by default, and these go to the _handle_PacketIn() function. We just need to get the raw data out and send this to the TAP interface</div>
<div>
<br /></div>
<div>
My switch always sends VLAN-tagged packets, so if yours doesn't then you'll want to change this a bit. Here is the SendToTap() function:</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace;">def SendToTap():</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> # remove vlan header and rebuild</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> print "Forwarding packet"</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> v = packet.next</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> i = v.next</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> eth = ethernet(src=packet.src, dst=packet.dst, type=v.eth_type)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> print type(i)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> eth.set_payload(i)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> # first 4 bytes are 00 00 08 00 (null short, then IPv4 ethertype)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> totap = struct.pack('!bbbb', 0, 0, 8, 0) + eth.pack()</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> #print totap.encode('hex')</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> self.tap.write(totap)</span></div>
</div>
<div>
<br /></div>
<div>
And we call this when a packet comes to us with a multicast MAC or our MAC:</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace;">if packet.dst == EthAddr(self.tap.hwaddr):</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> print "Packet for us!"</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> SendToTap()</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> return</span></div>
</div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace;">if packet.dst.isMulticast():</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> SendToTap()</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> flood() # 3a</span></div>
</div>
<div>
<br /></div>
<div>
Now the tunnel is all good to go. Just make sure any devices plugged into the switch have an MTU of 1300, and you can talk to the controller, transfer files off with SCP (30 minutes to copy an Ubuntu ISO at around 4Mb/s)</div>
<h2>
A couple of hiccups</h2>
<div>
<br /></div>
<h3>
Packet sizes</h3>
<div>
My switch doesn't seem to handle having the packet-size value changed. POX by default tells the switch to send the first 128 bytes of packets, and while we can send messages to increase this, they're ignored. The work-around is to change DEFAULT_MISS_SEND_LEN to 1400 in pox/openflow/libopenflow01.py</div>
<div>
<br /></div>
<h3>
Jitter</h3>
<div>
Latency varies from 1ms to 50ms, and TCP really, really doesn't like this. UDP routing protocols like OSPF shouldn't notice this, and even TCP-based routing protocols like BGP should be fine - but TCP gets really confused and this means you shouldn't expect any large data flows to work well with this.</div>
<div>
<br /></div>
<h3>
MTU sizes</h3>
<div>
This stuff confuses me. I'm a network engineer, and I'm supposed to know this stuff, but I don't. When we read from the TAP device, we read the MTU + 24 bytes. There's 14 bytes for the Ethernet header, 4 bytes for the TAP header, and another 6 bytes in there for no obvious reason. 24 bytes just seems to work, and I have no idea why.</div>
<div>
<br /></div>
<h3>
TAP device</h3>
<div>
Two things bug me about this - there doesn't seem to be a nice way to bring it up (apart from using ifconfig), and you need root to create it in the first place - I'd want to fix both of these for a nicer solution</div>
<h2>
Next steps</h2>
<div>
<ul>
<li>TAP devices could be created for each physical port on an OpenFlow device, or as routed interfaces for each VLAN - limitless opportunities here</li>
<li>BIRD or Quagga could bind to a TAP device, and the controller could turn routes into flows. BIRD has a python interface, but since both use standard routing protocols, you could easily sniff the traffic and build routing tables out of these. Sniffing BGP updates is still way easier than trying to build a Python TCP stack</li>
<li>VRFs? Traffic injection? Just another example of how easy it is to grab POX and do novel things with inexpensive hardware</li>
</ul>
</div>
Sam Russellhttp://www.blogger.com/profile/03431137767320819102noreply@blogger.com1tag:blogger.com,1999:blog-7532990877498512287.post-13071418962264147862012-08-31T20:17:00.001-07:002012-08-31T20:17:37.820-07:00ARP and ping in POX - Building a POX-based OpenFlow router<h2>
What are we doing?</h2>
<div>
Today, we're going to look at how to handle ARP and ICMP ping messages in the OpenFlow controller POX. The results aren't amazing - latency is between 5 and 50 milliseconds (using pypy makes no difference) - but it's an important feature for any layer 3 device.</div>
<h2>
Why is it important?</h2>
<div>
If we want to make native router modules in OpenFlow, we need to be able to assign IP addresses to interfaces on our device. This means the router can talk IP to other devices on the network, a vital step towards building an OpenFlow router.</div>
<h2>
What about RouteFlow?</h2>
<div>
RouteFlow is a fully-functional OpenFlow router that you can use today, that translates the physical ports on your OpenFlow device to interfaces on a virtual machine. This virtual machine runs a software router daemon like Quagga or BIRD, meaning you can leverage a mature software router instead of making your own.</div>
<div>
<br /></div>
<div>
RouteFlow represents an important step in OpenFlow routing, but I think we can do better. RouteFlow polls the RIB on a virtual machine and translates that to OpenFlow, which means the router daemons don't know that they're talking to the controller.</div>
<div>
<br />If we build a clean interface, we can write POX modules for OSPF, IS-IS, BGP and the like, and let them talk directly to the controller.</div>
<h2>
How to make packets in POX</h2>
<div>
I love the packet library in POX, it's clean and easy to use. To make your own packets, just do what your network stack normally does - create the payload, wrap that in the layer below, then the layer below that, and once you're at Ethernet you're finished.</div>
<h2>
Step 1: ARP replies</h2>
<div>
<div>
I've started with the forwarding.l2_learning module from POX, and added some code to the _handle_PacketIn function, just under self.macToPort[packet.src] = event.port (so that MAC addresses are still stored for each new port).</div>
<div>
<br /></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">match = of.ofp_match.from_packet(packet)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">if ( match.dl_type == packet.ARP_TYPE and</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span> match.nw_proto == arp.REQUEST and</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span> match.nw_dst == IPAddr("10.1.1.253")):</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> self.RespondToARP(packet, match, event)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> return</span></div>
</div>
<div>
<br /></div>
<div>
This checks for ARP requests for our hardcoded IP 10.1.1.253, and responds. The code to respond is as follows:</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> def RespondToARP(self, packet, match, event):</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> # reply to ARP request</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> r = arp()</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> r.opcode = arp.REPLY</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> r.hwdst = match.dl_src</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> r.protosrc = IPAddr("10.1.1.253")</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> r.protodst = match.nw_src</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> r.hwsrc = EthAddr("00:12:34:56:78:90")</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> e = ethernet(type=packet.ARP_TYPE, src=r.hwsrc, dst=r.hwdst)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> e.set_payload(r)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> log.debug("%i %i answering ARP for %s" %</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> ( event.dpid, event.port,</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> str(r.protosrc)))</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> msg = of.ofp_packet_out()</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> msg.data = e.pack()</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> msg.actions.append(of.ofp_action_output(port =</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> of.OFPP_IN_PORT))</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> msg.in_port = event.port</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> event.connection.send(msg)</span></div>
</div>
<div>
<br /></div>
<div>
We build an ARP packet by calling the arp() function from pox.lib.packet, and it initialises the packet as follows:</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace;">def __init__(self, raw=None, prev=None, **kw):</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> packet_base.__init__(self)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> self.prev = prev</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> self.hwtype = arp.HW_TYPE_ETHERNET</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> self.prototype = arp.PROTO_TYPE_IP</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> self.hwsrc = ETHER_ANY</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> self.hwdst = ETHER_ANY</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> self.hwlen = 6</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> self.opcode = 0</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> self.protolen = 4</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> self.protosrc = IP_ANY </span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> self.protodst = IP_ANY</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> self.next = b''</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> if raw is not None:</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> self.parse(raw)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> self._init(kw)</span></div>
</div>
<div>
<br /></div>
<div>
We just need to set the OPCODE, HWSRC, HWDST, PROTOSRC and PROTODST fields of this. I've done this in the body of the code, but we can simplify it by passing extra arguments as follows:</div>
<div>
<br /></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">r = arp( opcode=arp.REPLY, </span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> hwsrc=</span><span style="font-family: 'Courier New', Courier, monospace;">EthAddr("00:12:34:56:78:90"),</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> </span><span style="font-family: 'Courier New', Courier, monospace;">hwdst=match.dl_src,</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> </span><span style="font-family: 'Courier New', Courier, monospace;">protosrc = IPAddr("10.1.1.253"),</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> </span><span style="font-family: 'Courier New', Courier, monospace;">protodst = match.nw_src</span><span style="font-family: 'Courier New', Courier, monospace;">)</span></div>
<div>
<br /></div>
<div>
Once we've created the ARP packet, we need to create an Ethernet packet to put this into. This isn't perfect (we should check for VLAN tags and add them, or steal the body of the original packet and modify that), but it works if we're just dealing with a straight Ethernet network.</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace;">e = ethernet(type=packet.ARP_TYPE, src=r.hwsrc, dst=r.hwdst)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">e.set_payload(r)</span></div>
</div>
<div>
<br /></div>
<div>
Then we send this off to the controller, which sends it out the port it came through. Now we have an IP address that people can find, let's make it respond to something.</div>
<h2>
Step 2: Ping replies</h2>
<div>
If we can reply to ARP requests, we can reply to pings. This has a few more layers, but that just makes the code a little longer, not any more complicated.</div>
<div>
<br /></div>
<div>
The ARP reply is easy - we make an ARP packet, then put that in an Ethernet packet. For ping reply, this is what we do:</div>
<div>
<ol>
<li>Get the payload from the echo request (ping)</li>
<li>Create an echo reply packet, insert the old payload</li>
<li>Create an ICMP packet, insert the echo reply</li>
<li>Create an IPv4 packet, insert the ICMP</li>
<li>Create an Ethernet packet, insert the IPv4</li>
</ol>
<div>
Here's what the code looks like:</div>
</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> def RespondToPing(self, ping, match, event):</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> </span><span style="font-family: 'Courier New', Courier, monospace;">p = ping</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> # we know this is an ICMP Echo packet, so loop through</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> # maybe this needs a try... except?</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> while not isinstance(p, echo):</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> p = p.next</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> </span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> r = echo(id=p.id, seq=p.seq)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> r.set_payload(p.next)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> i = icmp(type=0, code=0)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> i.set_payload(r)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> ip = ipv4(protocol=ipv4.ICMP_PROTOCOL,</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> srcip=IPAddr("10.1.1.253"),</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> dstip=match.nw_src)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> ip.set_payload(i)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> e = ethernet(type=ping.IP_TYPE,</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> src=match.dl_dst,</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> dst=match.dl_src)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> e.set_payload(ip)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> log.debug("%i %i answering PING for %s" %</span><span style="font-family: 'Courier New', Courier, monospace;"> (</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> event.dpid, event.port,</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> str(match.nw_src)))</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> msg = of.ofp_packet_out()</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> msg.data = e.pack()</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> msg.actions.append(of.ofp_action_output(port =</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> of.OFPP_IN_PORT))</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> msg.in_port = event.port</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> event.connection.send(msg)</span></div>
</div>
<div>
<br /></div>
<div>
Simple, just slightly longer than the ARP code.</div>
<h2>
Pictures</h2>
<div>
Here's a look at the controller output, and the view from Wireshark.</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiXr-1qNKo8o73Tq1-kL2VJ7UhtJfNLY1yuh84tIx3oQD6yNzOksZSANR1fD0kdHBpoEWJtIxCtH19kgAUBjsrlxrweQ-3ER4HFGFUMkveQWdi7LIbj7Wb58pkmAjP05Jg5sS6UT1gYOFTX/s1600/Screen+Shot+2012-08-31+at+9.54.48+AM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="116" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiXr-1qNKo8o73Tq1-kL2VJ7UhtJfNLY1yuh84tIx3oQD6yNzOksZSANR1fD0kdHBpoEWJtIxCtH19kgAUBjsrlxrweQ-3ER4HFGFUMkveQWdi7LIbj7Wb58pkmAjP05Jg5sS6UT1gYOFTX/s400/Screen+Shot+2012-08-31+at+9.54.48+AM.png" width="400" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiL9UioJhTCY9Rc2xCsbela4NundDHp4iF9GWZivLxbR9Xnr84YGY-3jQbwG0uFwqLGgJI4-967N356bLTfVnpvWkEQcWGh1oK8733kYZokOyj047RKJdu8AoFzcjoua7SURjqDZsoGGcY3/s1600/Screen+Shot+2012-08-31+at+9.55.17+AM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="48" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiL9UioJhTCY9Rc2xCsbela4NundDHp4iF9GWZivLxbR9Xnr84YGY-3jQbwG0uFwqLGgJI4-967N356bLTfVnpvWkEQcWGh1oK8733kYZokOyj047RKJdu8AoFzcjoua7SURjqDZsoGGcY3/s400/Screen+Shot+2012-08-31+at+9.55.17+AM.png" width="400" /></a></div>
<div>
The <a href="http://www.openflow.org/wk/index.php/OpenFlow_Wireshark_Dissector">OpenFlow dissector</a> for Wireshark is part of the OpenFlow reference switch. It's a few years old, and uses an obselete API call - I can put up a patch if anyone gets stuck.</div>
<h2>
Next steps</h2>
<div>
<ul>
<li>ARP tables - if we're going to route traffic, we need to find the MAC addresses of destination IPs so that we send traffic to them</li>
<li>Routing protocol - RIP and OSPF will be fairly easy, BGP will be a bit harder due to relying on TCP. These can all be added to POX as modules</li>
<li>TUN/TAP support - we can create TUN/TAP interfaces and let the linux TCP stack do the hard work for us. This means a BGP module would create the TUN/TAP interface and handle OpenFlow encapsulation/decapsulation, but could offload the TCP to the Linux stack.</li>
</ul>
</div>
Sam Russellhttp://www.blogger.com/profile/03431137767320819102noreply@blogger.com3tag:blogger.com,1999:blog-7532990877498512287.post-21004947049993882632012-08-21T12:38:00.000-07:002012-08-31T19:05:29.166-07:00DjangoFlow part two: Quick and simple UI<h2>
In the last episode...</h2>
<div>
My previous blog post showed how to integrate POX with Django, and didn't have too much colour. It took a bit of playing to get it to integrate for the first time, but now it's done, it is incredibly easy to do cool stuff with this software combo, and make new apps for easy OpenFlow access.</div>
<h2>
Part 2: An actual User Interface</h2>
<div>
I put in about 10 minutes extra just so I could give you all some lovely pictures, and here's what it all looks like:</div>
<div>
<br /></div>
<div>
This is all Django - I've just turned on the admin interface. Here we can add and remove flows from the database.</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEggUL8ODXq9ejPJ2Q3y8CY-R6yN7UrkhdFp8Y478-5CDBRAQSH5hyphenhyphenBEmMJUkTWchvnLIUQENAcva97a0-qqPCkbd-6AngvCTU-FeNY2p_9aHwUVFJaCzmXaIyi1EA79QaDyWaVpvmKtV3Wt/s1600/Screen+Shot+2012-08-22+at+7.23.32+AM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="145" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEggUL8ODXq9ejPJ2Q3y8CY-R6yN7UrkhdFp8Y478-5CDBRAQSH5hyphenhyphenBEmMJUkTWchvnLIUQENAcva97a0-qqPCkbd-6AngvCTU-FeNY2p_9aHwUVFJaCzmXaIyi1EA79QaDyWaVpvmKtV3Wt/s400/Screen+Shot+2012-08-22+at+7.23.32+AM.png" width="400" /></a></div>
<br />
Here's a list of the two flows that I put in for testing<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXSDcxMjAd4MHGnPbDg9Z1JBRxFkZ7hNibdWo7ebKtQ3pexHWVsJldRnmdInt4qyhcPIg8oBKNP-cw4p-GbpGGvGizrBT5MudDrL8ba1_mxgjw9FhsXPSc4JDfKzJ4U7IzM9LFHXLWD5Jn/s1600/Screen+Shot+2012-08-22+at+7.23.41+AM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="115" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXSDcxMjAd4MHGnPbDg9Z1JBRxFkZ7hNibdWo7ebKtQ3pexHWVsJldRnmdInt4qyhcPIg8oBKNP-cw4p-GbpGGvGizrBT5MudDrL8ba1_mxgjw9FhsXPSc4JDfKzJ4U7IzM9LFHXLWD5Jn/s400/Screen+Shot+2012-08-22+at+7.23.41+AM.png" width="400" /></a></div>
<br />
And here's the output from my OpenFlow switch.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEie-qK0wZ9lc16cDv41kwbX2Vq8xQJpDEgRSgPKnLhjJHf383aaEE7aDHHJC0BQf37DOzWxeN0-eFUrfhfhZDkmb8pq5AltXuvAGli4qIPSUIolQasECPDR53k6_V15hJpoRgxA2h-QJngP/s1600/Screen+Shot+2012-08-22+at+7.34.32+AM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="32" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEie-qK0wZ9lc16cDv41kwbX2Vq8xQJpDEgRSgPKnLhjJHf383aaEE7aDHHJC0BQf37DOzWxeN0-eFUrfhfhZDkmb8pq5AltXuvAGli4qIPSUIolQasECPDR53k6_V15hJpoRgxA2h-QJngP/s400/Screen+Shot+2012-08-22+at+7.34.32+AM.png" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div>
<br /></div>
<div>
It doesn't update in real time, but you can manipulate the database via the interweb and push those flows directly out to your switch. Making this update in real time is going to be another half-hour's work, tops.</div>
<h2>
What's new?</h2>
<div>
I changed the model a bit - now we can choose ports as well. Remember to delete your old database before syncing again or else it'll get upset</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;">class Flow(models.Model):</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>internalip = models.CharField(max_length=200)</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>externalip = models.CharField(max_length=200)</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>internalport = models.IntegerField()</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>externalport = models.IntegerField()</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>idletime = models.IntegerField()</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>hardtime = models.IntegerField()</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>def __unicode__(self):</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>return "Internal: IP=" + self.internalip + " port=" + str(self.internalport) +", External: IP=" + self.externalip + " port=" + str(self.externalport)</span></div>
</div>
<div>
<br /></div>
<div>
The code in l2_learning.py got a bit of a birthday too:</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;">class LearningSwitch (EventMixin):</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> def __init__ (self, connection, transparent):</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> # Switch we'll be adding L2 learning switch capabilities to</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> self.connection = connection</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> self.transparent = transparent</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> # Our table</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> self.macToPort = {}</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> # We want to hear PacketIn messages, so we listen</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> self.listenTo(connection)</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> #log.debug("Initializing LearningSwitch, transparent=%s",</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> # str(self.transparent))</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> </span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> # add new flows by default</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> for flow in Flow.objects.all():</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>self.AddFlowFromModel(flow)</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> </span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> </span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> def AddFlowFromModel(self, flow):</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> # add outgoing flow</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> msg = of.ofp_flow_mod()</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> msg.match = of.ofp_match()</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> msg.match.dl_type = ethernet.IP_TYPE</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> msg.match.nw_src = str(flow.internalip)</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> msg.match.nw_dst = str(flow.externalip)</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> msg.match.in_port = flow.internalport</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> msg.idle_timeout = flow.idletime</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> msg.hard_timeout = flow.hardtime</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> msg.actions.append(of.ofp_action_output(port = flow.externalport))</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> self.connection.send(msg)</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> </span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> # add incoming flow</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> msg = of.ofp_flow_mod()</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> msg.match = of.ofp_match()</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> msg.match.dl_type = ethernet.IP_TYPE</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> msg.match.nw_src = str(flow.externalip)</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> msg.match.nw_dst = str(flow.internalip)</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> msg.match.in_port = flow.externalport</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> msg.idle_timeout = flow.idletime</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> msg.hard_timeout = flow.hardtime</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> msg.actions.append(of.ofp_action_output(port = flow.internalport))</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> self.connection.send(msg)</span></div>
</div>
<div>
<br /></div>
<div>
After this, it was just a case of enabling the admin interface as per <a href="https://docs.djangoproject.com/en/dev/intro/tutorial02/">https://docs.djangoproject.com/en/dev/intro/tutorial02/</a> - this is our admin.py:</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;">from django.contrib import admin</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;">from flew.models import Flow</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;">admin.site.register(Flow)</span></div>
</div>
<div>
<br /></div>
And for the sake of completeness, here's how to start them - the web interface is<br />
<br />
<span style="font-family: 'Courier New', Courier, monospace;">python manage.py runserver</span><br />
<br />
and the controller is<br />
<br />
<span style="font-family: 'Courier New', Courier, monospace;">python pox.py forwarding.l2_learning</span><br />
<h2>
What next?</h2>
<div>
I don't know... I thought this would be much harder? Authentication will be fun, some way to dynamically check the database and update flows in real time (and remove them maybe) - this is left as an exercise for the reader though</div>
Sam Russellhttp://www.blogger.com/profile/03431137767320819102noreply@blogger.com1tag:blogger.com,1999:blog-7532990877498512287.post-21741936706714684562012-08-21T02:31:00.000-07:002012-08-31T19:05:47.683-07:00DjangoFlow - Web UI for the POX OpenFlow controller<h2>
The Plan</h2>
<div>
OpenFlow is a simple concept - an open interface to switch and router hardware. Can we tie this into an open web framework to create a foundation for a really simple Web UI?</div>
<h2>
The tools</h2>
<div>
I've been learning how to use Django recently, and it seems like a perfect choice for this task. It's written in Python, so it integrates easily with the POX controller. I've also used virtualenv to make it more portable - this means the project is largely self-contained and can be copied to a new system by simply copying the folder.</div>
<h2>
Implementation</h2>
<div>
To start, I created a virtualenv called "djangoflow" on an Ubuntu machine and installed django in it. There are lots of ways to do this - do a google for "django setup virtualenv ubuntu" and you'll get tons of hits.</div>
<div>
<br /></div>
<div>
I made a project called mysite, and an app called flew (NZ english for "flow"), and left that bit for the time being.</div>
<div>
<br /></div>
<div>
The folder structure then looks something like this:</div>
<div>
<br /></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">djangoflow</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">- bin</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">- include</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">- lib</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">- local</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">- mysite</span></div>
<span style="font-family: Courier New, Courier, monospace;">--- flew</span><br />
<div>
<span style="font-family: Courier New, Courier, monospace;">--- mysite</span></div>
<div>
<br /></div>
<div>
I then did a git clone of the latest build of POX from the NOXREPO github, into the djangoflow folder, and then copied everything from the base mysite folder into the pox folder. The folder structure then looks like:</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace;">djangoflow</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">- bin</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">- include</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">- lib</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">- local</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">- pox</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">--- .git</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">--- flew</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">--- mysite</span></div>
</div>
<div>
<span style="font-family: Courier New, Courier, monospace;">--- pox</span></div>
<div>
<br /></div>
<div>
This is great - now back to Django.</div>
<div>
<br /></div>
<div>
The model that I used was really simple, here's what I've got so far:</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace;">from django.db import models</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"># Create your models here.</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">class Flow(models.Model):</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>internalip = models.CharField(max_length=200)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>externalip = models.CharField(max_length=200)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>idletime = models.IntegerField()</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>hardtime = models.IntegerField()</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>def __unicode__(self):</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>return "Internal: " + self.internalip + ", External: " + self.externalip</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">class User(models.Model):</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>name = models.CharField(max_length=200)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">class Device(models.Model):</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>dpid = models.CharField(max_length=200)</span></div>
</div>
<div>
<br /></div>
<div>
We'll only use the Flow model today, the others are for expansion later. To make this work, we'll need to edit the settings in mysite.settings - set up the databases and installed_apps sections as follows:</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace;">DATABASES = {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> 'default': {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> 'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'.</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> 'NAME': 'flew.db', # Or path to database file if using sqlite3.</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> 'USER': '', # Not used with sqlite3.</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> 'PASSWORD': '', # Not used with sqlite3.</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> 'HOST': '', # Set to empty string for localhost. Not used with sqlite3.</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> 'PORT': '', # Set to empty string for default. Not used with sqlite3.</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> }</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">}</span></div>
</div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace;">INSTALLED_APPS = (</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> 'django.contrib.auth',</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> 'django.contrib.contenttypes',</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> 'django.contrib.sessions',</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> # 'django.contrib.sites',</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> 'django.contrib.messages',</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> 'django.contrib.staticfiles',</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> # Uncomment the next line to enable the admin:</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> 'django.contrib.admin',</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> # Uncomment the next line to enable admin documentation:</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> 'django.contrib.admindocs',</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> 'flew',</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">)</span></div>
</div>
<div>
<br /></div>
<div>
Awesome, now run syncdb (check against whatever tutorial you use to see how this is done) and populate the database. Now, fire up the shell and create your first flow:</div>
<div>
<br /></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">python manage.py shell</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">from flew.models import Flow</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">f = Flow(internalip = "10.1.10.2", externalip = "10.1.20.2", idletime = 300, hardtime = 3600)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">f.save()</span></div>
<div>
<br /></div>
<div>
If that all worked, then you'll have a new entry in your database. We are never going to access the database directly though - we can access all the Django goodness from POX.</div>
<div>
<br /></div>
<div>
Open up pox/forwarding/l2_learning.py and have a look through - if you've used this before, then good, if not, then see what it all does.</div>
<div>
<br /></div>
<div>
I've hijacked this just for this example, but it sets a starting point for any POX-Django integrated tools. Make sure your imports section looks like this:</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace;"># import django stuff</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">from django.core.management import setup_environ</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">from mysite import settings</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">setup_environ(settings)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">from flew.models import Flow</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">from pox.core import core</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">import pox.openflow.libopenflow_01 as of</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">from pox.lib.revent import *</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">from pox.lib.util import dpidToStr</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">from pox.lib.util import str_to_bool</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">from pox.lib.packet import ethernet</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">from pox.lib.packet import ipv4</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">import time</span></div>
</div>
<div>
<br /></div>
<div>
Then go down to the __init__ function for LearningSwitch and add this to the end:</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> # add new flow by default</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> flow1 = Flow.objects.all()[0]</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> msg = of.ofp_flow_mod()</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> msg.match = of.ofp_match()</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> msg.match.dl_type = ethernet.IP_TYPE</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> msg.match.nw_src = str(flow1.internalip)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> msg.match.nw_dst = str(flow1.externalip)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> msg.idle_timeout = flow1.idletime</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> msg.hard_timeout = flow1.hardtime</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> msg.actions.append(of.ofp_action_output(port = 1))</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> #msg.buffer_id = event.ofp.buffer_id # 6a</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> self.connection.send(msg)</span></div>
</div>
<div>
<br /></div>
<div>
What does this do? It takes the first Flow out of our database, creates a flow to allow traffic from internalip, going to externalip, to go out port 1, which should be pointing at the outside world. This flow will stay in the switch for an hour, or 5 minutes without being triggered - whichever happens first.</div>
<h2>
Question time</h2>
<div>
Q: Is it really this easy?</div>
<div>
A: Yes. Python is easy, Django is easy, Virtualenv is easy, POX is easy. It's just a little tricky making them all work together - that's what this guide is for.</div>
<div>
<br /></div>
<div>
Q: Why add flows this way when we already have a CLI?</div>
<div>
A: Django has a clean admin interface that I haven't covered here (check the setup tutorial on the Django website) - you can set flows in there, and every time a switch connects, it will use those flows.</div>
<div>
<br /></div>
<div>
Q: Could I start using this right now?</div>
<div>
A: Totally. If you want your switches to retrieve their configuration from the controller automatically when they start up, you can set a bunch of super specific flows here and roll it out now. If you want something a bit more clever, then you can jump into the Django and POX API and make it happen yourself.</div>
<div>
<br /></div>
<div>
Q: What next?</div>
<div>
A: There are two extra models that we didn't use - one for user authentication, and one for managing devices. If your OpenFlow devices connect by SSL (which they should in the real world) then you can create models for them that hold particular flows - this can all be managed via a web interface. As for user authentication, there are millions of ways to do this - you can set privileges for who can set certain flows, make requests that admins can approve - the possibilities are endless!</div>
Sam Russellhttp://www.blogger.com/profile/03431137767320819102noreply@blogger.com6