<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
<channel>
<title>iljitsch.com - program (en)</title>
<link>http://program.iljitsch.com/en/</link>
<language>en</language>
<description>Iljitsch van Beijnum's program posts (en)</description>

<item>
  <title>&#8594; The beauty of finished software</title>
  <description>&lt;blockquote&gt;Finished software is software that’s not expected to change, and that’s a feature! You can rely on it to do some real work.&lt;/blockquote&gt;
&lt;p&gt;

We need more of this.
&lt;p&gt;

But: how do you &lt;em&gt;write&lt;/em&gt; software that will keep working for decades to come? Certainly don&apos;t look at Apple for this, they keep changing their CPU architectures every decade or so and after a transition period, the old stuff is dead.
&lt;p&gt;

Could &lt;a href=&quot;https://en.wikipedia.org/wiki/WebAssembly&quot;&gt;WebAssembly&lt;/a&gt; be the solution? This is a pretty fast binary format that almost any programming language can be compiled to.</description>
  <link>https://josem.co/the-beauty-of-finished-software/</link>
  <guid isPermaLink="true">https://josem.co/the-beauty-of-finished-software/</guid>
  <pubDate>Wed, 01 Nov 2023 17:01:30 GMT</pubDate>
</item>

<item>
  <title>MySQL Unicode weirdness</title>
  <description>After looking at the SQLite Unicode behavior, it&apos;s now time to do the same for MySQL. Coincidentally, I&apos;m currently migrating some old databases that were created in the very early 2000s to a more modern environment. I think those old databases were from the MySQL 3.x days, before MySQL gained any sort of Unicode support. Those old tables are thus still in the latin1 (ISO 8859-1) character set.
&lt;p&gt;

For a long time that worked fine for me as these tables contain data in English and Dutch, and Dutch only needs a few accents, which are all present in Latin 1. However... at some point it started becoming more convenient to use UTF-8 for the text in those databases. So I did. I just never told MySQL about the switch.
&lt;p&gt;

In hindsight, that was not a terrible decision, as it avoided complexities at a time when UTF-8 support was still immature. But these days, there&apos;s really no excuse to do anything other than make an entire workflow UTF-8 clean, barring any showstoppers.
&lt;p&gt;

So I migrated an old table to the test environment for the new system. And got some really weird results: on web pages &quot;CO₂&quot; showed up as &quot;COâ‚‚&quot;, but in MySQL it showed up correct, be it that the number of characters is off (I only got 6 while I specified 8):
&lt;p&gt;

&lt;pre class=wrap&gt;
mysql&gt; select substr(article, 1587, 8) from muart where id = 753;
&lt;/pre&gt;
&lt;pre&gt;
+--------------------------+
| substr(article, 1587, 8) |
+--------------------------+
| CO₂ ui                 |
+--------------------------+
&lt;/pre&gt;
&lt;p&gt;

Further digging by looking at the raw data:
&lt;p&gt;

&lt;pre class=wrap&gt;
mysql&gt; select hex(substr(article, 1587, 8)) from muart where id = 753;
&lt;/pre&gt;
&lt;pre&gt;
+-------------------------------+
| hex(substr(article, 1587, 8)) |
+-------------------------------+
| 434FC3A2E2809AE2809A207569    |
+-------------------------------+
&lt;/pre&gt;
&lt;p&gt;

Fortunately, it&apos;s not necessary to decode this manually, there is an &lt;a href=&quot;https://software.hixie.ch/utilities/cgi/unicode-decoder/utf8-decoder&quot;&gt;UTF-8 Decoder&lt;/a&gt; web tool for that. The decoder shows that COâ‚‚ is correct. So why is MySQL showing me CO₂? That&apos;s a problem I hadn&apos;t heard of before, but is apparently not uncommon:
&lt;p&gt;

Double-encoded UTF-8.
&lt;p&gt;

This happens when you take UTF-8, such as &quot;CO₂&quot;, and then pretend it&apos;s another encoding (usually Latin 1) and convert that to UTF-8. So what had happened is that as I was importing my data in MySQL, the MySQL command line client would send UTF-8 to the MySQL server process, but the server would think that was Latin 1 and convert it to UTF-8. Then when I did a query, the server would convert the UTF-8 back to what it thought was Latin 1 for the convenience of the client, and the client then showed this as UTF-8 so everything &lt;em&gt;looked&lt;/em&gt; good, but was actually stored in the database incorrectly.
&lt;p&gt;

Fortunately, the two related problems were both easy enough to fix. First, make sure the client and server agree on the character encoding. Let&apos;s first check what the server&apos;s original settings are:
&lt;p&gt;

&lt;pre class=wrap&gt;
&lt;/pre&gt;
&lt;pre&gt;
mysql&gt; SHOW SESSION VARIABLES LIKE &apos;character_set%&apos;;
+--------------------------+--------------------------------+
| Variable_name            | Value                          |
+--------------------------+--------------------------------+
| character_set_client     | latin1                         |
| character_set_connection | latin1                         |
| character_set_database   | utf8mb4                        |
| character_set_filesystem | binary                         |
| character_set_results    | latin1                         |
| character_set_server     | utf8mb4                        |
| character_set_system     | utf8mb3                        |
| character_sets_dir       | /usr/share/mysql-8.0/charsets/ |
+--------------------------+--------------------------------+
&lt;/pre&gt;
&lt;p&gt;

Wow. But fortunately we don&apos;t have to fix all of those individually. We can simply do:
&lt;p&gt;

&lt;pre class=wrap&gt;
mysql --default_character_set=utf8mb4
&lt;/pre&gt;
&lt;p&gt;

And then:
&lt;p&gt;

&lt;pre class=wrap&gt;
mysql&gt; SHOW SESSION VARIABLES LIKE &apos;character_set%&apos;;
&lt;/pre&gt;
&lt;pre&gt;
+--------------------------+--------------------------------+
| Variable_name            | Value                          |
+--------------------------+--------------------------------+
| character_set_client     | utf8mb4                        |
| character_set_connection | utf8mb4                        |
| character_set_database   | utf8mb4                        |
| character_set_filesystem | binary                         |
| character_set_results    | utf8mb4                        |
| character_set_server     | utf8mb4                        |
| character_set_system     | utf8mb3                        |
| character_sets_dir       | /usr/share/mysql-8.0/charsets/ |
+--------------------------+--------------------------------+
&lt;/pre&gt;
&lt;p&gt;

Much better.
&lt;p&gt;

And convert the double-encoded UTF-8 into something more readable:
&lt;p&gt;

&lt;pre class=wrap&gt;
mysql&gt; UPDATE muart SET title=CONVERT(CAST(CONVERT(title USING latin1) AS binary) USING utf8mb4);
&lt;/pre&gt;
&lt;p&gt;

Keen-eyed observers may have noted that MySQL has two versions of UTF-8: utf8mb3 and utf8mb4. This goes back to the early days of Unicode, where the idea was that all characters would fit into 16 bits. That results in a maximum of 3 bytes of UTF-8. But it soon became clear that 16 bits wasn&apos;t enough. So now it&apos;s 21 bits. UTF-8 can handle that just fine, but those characters that need 17 - 21 bits result in 4-byte UTF-8 sequences. So when dealing with MySQL, when you think &quot;UTF-8&quot;, type &quot;utf8mb4&quot;.
</description>
  <link>http://www.iljitsch.com/2023/09-21-mysql-unicode-weirdness.html</link>
  <guid isPermaLink="true">http://www.iljitsch.com/2023/09-21-mysql-unicode-weirdness.html</guid>
  <pubDate>Thu, 21 Sep 2023 11:00:00 GMT</pubDate>
</item>

<item>
  <title>Looking at SQLite Unicode behavior</title>
  <description>In this post, I want to have a look at how SQLite interacts with Unicode. (Also see my post &lt;a href=&quot;https://www.iljitsch.com/2022/01-30-the-dark-magic-of-unicode.html&quot;&gt;The (dark) magic of Unicode&lt;/a&gt;.) As explained &lt;a href=&quot;https://sqlite.org/quirks.html#does_not_do_full_unicode_case_folding_by_default&quot;&gt;here&lt;/a&gt;, SQLite doesn&apos;t have full Unicode support unless that support is explicitly included when SQLite is compiled.
&lt;p&gt;

So what does this mean in practice?
&lt;p&gt;

On the (non-Windows) systems I have access to, SQLite uses UTF-8 to store text. UTF-16 is also supported. However, a limitation that&apos;s made very explicit is that changing from upper case to lower case or the other way around is &lt;em&gt;only&lt;/em&gt; supported for 7-bit ASCII. So the 26 letters we know from the English alphabet.
&lt;p&gt;

Let&apos;s see for ourselves:
&lt;p&gt;

&lt;pre class=wrap&gt;% sqlite3 test.db&lt;/pre&gt;
&lt;pre class=wrap&gt;sqlite&gt; .mode qbox&lt;/pre&gt;
&lt;pre class=wrap&gt;sqlite&gt; create table test (id integer primary key, text text);&lt;/pre&gt;
&lt;pre class=wrap&gt;sqlite&gt; insert into test (text) values (format(&apos;SM%sRG%sSBORD&apos;, &apos;O&apos;, &apos;A&apos;));&lt;/pre&gt;
&lt;pre class=wrap&gt;sqlite&gt; insert into test (text) values (format(&apos;SM%sRG%sSBORD&apos;, char(0xD6), char(0xC5)));&lt;/pre&gt;
&lt;pre class=wrap&gt;sqlite&gt; insert into test (text) values (format(&apos;SM%sRG%sSBORD&apos;, &apos;O&apos;||char(0x308), &apos;A&apos;||char(0x30A)));&lt;/pre&gt;
&lt;pre class=wrap&gt;sqlite&gt; insert into test (text) values (format(&apos;SM%sRG%sSBORD&apos;, &apos;U&apos;, &apos;A&apos;));&lt;/pre&gt;
&lt;p&gt;

We&apos;ve now added four variations of the word &quot;SMORGASBORD&quot; to our test database table. And that worked:
&lt;p&gt;

&lt;pre&gt;sqlite&gt; select * from test;
┌────┬─────────────────┐
│ id │      text       │
├────┼─────────────────┤
│ 1  │ &apos;SMORGASBORD&apos;   │
│ 2  │ &apos;SMÖRGÅSBORD&apos;   │
│ 3  │ &apos;SMÖRGÅSBORD&apos; │
│ 4  │ &apos;SMURGASBORD&apos;   │
└────┴─────────────────┘
&lt;/pre&gt;
&lt;p&gt;

The first version uses an unaccented O and A. The second version uses the &quot;composed&quot; version of the accented letters. That means that a single Unicode character / code point encodes the letter/accent combination. The third version uses the regular letters followed by a &quot;combining diacritical mark&quot;, a special Unicode character that adds an accent to the previous character. And then finally I added &quot;SMURGASBORD&quot; which will come in handy later.
&lt;p&gt;

(The combining diacritical marks throw off the SQLite text length / box size calculations, hence the misplaced vertical line at the end of row 3.)
&lt;p&gt;

Those upper case letters are a bit overbearing, though, so let&apos;s convert them to lower case.
&lt;p&gt;

&lt;pre class=wrap&gt;sqlite&gt; update test set text = lower(text);&lt;/pre&gt;
&lt;pre&gt;sqlite&gt; select * from test;
┌────┬─────────────────┐
│ id │      text       │
├────┼─────────────────┤
│ 1  │ &apos;smorgasbord&apos;   │
│ 2  │ &apos;smÖrgÅsbord&apos;   │
│ 3  │ &apos;smörgåsbord&apos; │
│ 4  │ &apos;smurgasbord&apos;   │
└────┴─────────────────┘
&lt;/pre&gt;
&lt;p&gt;

And here SQLite&apos;s lower()/upper() limitation pops up. The first and last rows are converted without trouble because those only use the letters out of the regular Latin script alphabet. The second one uses letter/accent combinations outside of the 7-bit ASCII range so those letters are not converted to lower case. Interestingly, the third row did get converted. That&apos;s because the &lt;em&gt;letter&lt;/em&gt; part is encoded using the regular O and A, which SQLite &lt;em&gt;can&lt;/em&gt; convert to lower case. The diacritical combing marks that add the accents then apply to the now lower case o and a without missing a beat.
&lt;p&gt;

So the take home message is: don&apos;t let SQLite convert between upper and lower case.
&lt;p&gt;

In any event, the rules for how to do this properly vary by language. For instance, an English speaker may think the following makes sense:
&lt;p&gt;

&lt;pre&gt;
istanbul = Istanbul
ijsbeer  = Ijsbeer
één      = Één
&lt;/pre&gt;
&lt;p&gt;

But that would be wrong. In Turkish, there is a dotted i and an un-dotted i, the ı. Ok, that&apos;s not so bad. Until you realize that i becomes İ and ı becomes I. And in Dutch an &quot;ijsbeer&quot; (polar (ice) bear) needs to become &quot;IJsbeer&quot; at the start of a sentence because IJ is considered a single letter. And, unlike for instance the French, we don&apos;t put accents on capital letters, so &quot;één&quot; (one) at the beginning of a sentence becomes &quot;Eén&quot;. So these are correct:
&lt;p&gt;

&lt;pre&gt;
istanbul = İstanbul
ijsbeer  = IJsbeer
één      = Eén
&lt;/pre&gt;
&lt;p&gt;

Also, because the composed and decomposed variations of smörgåsbord use different sequences of Unicode characters, if you compare the same word with those different encodings, they won&apos;t match. But that part can be solved by normalizing your Unicode string to either composed or decomposed, and then comparisons will work.
&lt;p&gt;

But then there&apos;s sorting to worry about.
&lt;p&gt;

&lt;pre class=wrap&gt;sqlite&gt; select id, length(text) as chars, octet_length(text) bytes, text from test order by text;&lt;/pre&gt;
&lt;pre&gt;┌────┬───────┬───────┬─────────────────┐
│ id │ chars │ bytes │      text       │
├────┼───────┼───────┼─────────────────┤
│ 1  │ 11    │ 11    │ &apos;smorgasbord&apos;   │
│ 3  │ 13    │ 15    │ &apos;smörgåsbord&apos; │
│ 4  │ 11    │ 11    │ &apos;smurgasbord&apos;   │
│ 2  │ 11    │ 13    │ &apos;smÖrgÅsbord&apos;   │
└────┴───────┴───────┴─────────────────┘
&lt;/pre&gt;
&lt;p&gt;

(We can see here that &quot;length&quot; shows the number of Unicode code points. &quot;octet_length&quot; is the length in bytes, which is different as soon as a text contains anything that isn&apos;t 7-bit ASCII.)
&lt;p&gt;

Considering what we discussed before, this makes perfect sense: &quot;smo&quot; and &quot;smo&quot; come before &quot;smu&quot; (the fact that the second &quot;smo&quot; is followed by a diacritical combining mark is not relevant when looking at the first three characters) and then &quot;smÖ&quot; comes after &quot;smu&quot; because Ö is a &quot;high-ASCII&quot; character while &quot;u&quot; is just a regular 7-bit ASCII character.
&lt;p&gt;

Of course this is not really a &lt;em&gt;correct&lt;/em&gt; sorting, as that again depends on the language/locale. In some languages, letters with accents are considered letters in their own right with a specific place in the sorting order, while in other languages, the accent is simply ignored and äbc comes between abb and abd. And that&apos;s just for &quot;general&quot; sorting.
&lt;p&gt;

Name/phonebook sorting often has its own rules. For instance, so many people here in the Netherlands have a name starting with &quot;van&quot; (of) or &quot;van de&quot; (of the) that these prefixes are generally ignored for name sorting. And many names have an &quot;ij&quot; and a &quot;y&quot; form. So &quot;van Beijnum&quot; is found under B, and is sorted together with &quot;van Beynum&quot;.
&lt;p&gt;

So these caveats are something to be aware of. But there is an escape hatch: you can compile SQLite with SQLITE_ENABLE_ICU to gain full Unicode support. But this makes SQLite a good deal bigger and slower. Dilemmas...</description>
  <link>http://www.iljitsch.com/2023/09-05-looking-at-sqlite-unicode-behavior.html</link>
  <guid isPermaLink="true">http://www.iljitsch.com/2023/09-05-looking-at-sqlite-unicode-behavior.html</guid>
  <pubDate>Tue, 05 Sep 2023 16:03:42 GMT</pubDate>
</item>

<item>
  <title>SQLite: add a powerful database engine to any app</title>
  <description>When I was 24, I decided to give up my job and go to college and study computer science. If I&apos;d have known how many database classes that involved, maybe I would have reconsidered.
&lt;p&gt;

Back then, we had a big server that ran a RDBMS (relational database management system) that hundreds of students all used together. These systems were big, complex and &lt;em&gt;expensive&lt;/em&gt;. (Oracle made its fortune selling RDBMSes.) MySQL and PostgresQL are somewhat more streamlined free and open source RDBMSes. Much better, but firewalling, user authentication and backups are still somewhat of a headache. But hey, if you need a database, you need a database.
&lt;p&gt;

Enter SQLite.
&lt;p&gt;

Traditional RDBMSes are a &lt;em&gt;service&lt;/em&gt; that you connect to over the network—even if the RDBMS runs on the local machine. SQLite, on the other hand, is just a (pretty small) library that you link to in your software, and all the database contents go into a single file stored on the local file system. But you still get a very complete implementation of SQL, the &quot;structured query language&quot; that is used to interact with most databases.
&lt;p&gt;

I find myself dealing with &lt;a href=&quot;https://en.wikipedia.org/wiki/Comma-separated_values&quot;&gt;CSV files&lt;/a&gt; and the like pretty regularly. For just getting data in and out of applications, this is usually fine. But doing anything useful with CSV data in your own scripts or applications is a chore. With SQL, on the other hand, you can easily analyse and transform data, often with pretty simple SQL queries.
&lt;p&gt;

&lt;h2&gt;Compared to MySQL&lt;/h2&gt;
&lt;p&gt;

In the past, I&apos;ve installed MySQL on my laptop to some &lt;a href=&quot;//www.bgpexpert.com/presentations/AS%20paths%20long%20longer%20longest.pdf&quot;&gt;data analysis&lt;/a&gt;. Having to install a big piece of software and configuring a user account and permissions is less than ideal. With SQLite you just point to a local file and you&apos;re in business. &lt;a href=&quot;https://stackoverflow.com/questions/1711631/improve-insert-per-second-performance-of-sqlite&quot;&gt;If you know what you&apos;re doing&lt;/a&gt;, SQLite can also be a lot faster than many other database engines such as MySQL. (Although without grouping large numbers of insert statements into larger &lt;a href=&quot;https://en.wikipedia.org/wiki/Database_transaction#In_SQL&quot;&gt;transactions&lt;/a&gt; SQLite will be slow.)
&lt;p&gt;

Se the page &lt;a href=&quot;https://www.sqlite.org/whentouse.html&quot;&gt;Appropriate Uses For SQLite&lt;/a&gt; on the SQLite website to learn when SQLite is a good fit and when it isn&apos;t. The main thing is that SQLite is not a good fit when the application needs to access the database over a network.
&lt;p&gt;

&lt;h2&gt;Using SQLite with PHP&lt;/h2&gt;
&lt;p&gt;

Initially, it seemed that moving from MySQL to SQLite in PHP &lt;a href=&quot;https://phpdelusions.net/pdo&quot;&gt;would be tricky&lt;/a&gt;. However, the basics are no issue at all. This is the simple way to use MySQL in PHP:
&lt;p&gt;

&lt;pre&gt;
$db = mysqli_connect(&quot;localhost&quot;, &quot;db_user&quot;, &quot;secretpw&quot;, &quot;db_name&quot;);
$result = $db-&gt;query(&quot;select * from pages where pagenum = $n&quot;);
while ($row = $result-&gt;fetch_assoc())
  echo json_encode($row);
$db-&gt;close();
&lt;/pre&gt;
&lt;p&gt;

That translates relatively neatly into SQLite:
&lt;p&gt;

&lt;pre&gt;
$db = new SQLite3(&quot;content.db&quot;);
$result = $db-&gt;query(&quot;select * from pages where pagenum = $n&quot;);
if ($row = $result-&gt;fetchArray())
  echo json_encode($row);
$db-&gt;close();
&lt;/pre&gt;
&lt;p&gt;

The &lt;a href=&quot;https://www.sqlite.org/quickstart.html&quot;&gt;C API&lt;/a&gt; requires a few more steps to get things done, but doesn&apos;t look excessively complicated.
However, MySQL and SQLite use different SQL dialects. So far, my main issue has been the missing left() and right() functions and the handling of &lt;a href=&quot;https://www.sqlite.org/lang_datefunc.html&quot;&gt;dates/times&lt;/a&gt;. Especially adjusting a given date is done in rather different ways.
&lt;p&gt;

&lt;h2&gt;SQLite from the command line&lt;/h2&gt;
&lt;p&gt;

You can directly interact with SQLite databases through the sqlite3 program that is installed by default on a good number of operating systems. You can download the Windows version from the &lt;a href=&quot;https://www.sqlite.org/download.html&quot;&gt;sqlite.org&lt;/a&gt; version. And there&apos;s even an &lt;a href=&quot;https://aminet.net/package/biz/dbase/sqlite-3.34.0-amiga&quot;&gt;Amiga port&lt;/a&gt;. Sweet. Just type &quot;sqlite3&quot; followed by the filename of a database file and you&apos;re in business. SQLite 3 is open source, and interestingly, has no license: it&apos;s entirely in the public domain.
&lt;p&gt;

A features in SQLite that I really like is that you can read &lt;a href=&quot;https://en.wikipedia.org/wiki/Binary_large_object&quot;&gt;blobs&lt;/a&gt; from the file system in order to store them in a database table, and then later write those back to the file system:
&lt;p&gt;

insert into blobstore (id, data) values (1, readfile(&apos;Makefile&apos;));
select writefile(&apos;blob.bin&apos;, data) from blobstore where id = 1;
&lt;p&gt;

And another great feature of the command line sqlite3 program is that you can just call an editor to edit the contents of a field. This is especially useful if you store larger texts in a database. It works like this:
&lt;p&gt;

&lt;pre&gt;
% sqlite3 test.db
SQLite version 3.28.0 2019-04-15 14:49:49
Enter &quot;.help&quot; for usage hints.
sqlite&gt; update blobstore set data = edit(data, &apos;vi&apos;) where id = 1;
&lt;/pre&gt;
&lt;p&gt;

(Of course in general you&apos;d probably edit text columns, not blob columns.)
&lt;p&gt;

&lt;h2&gt;Portability and easy backups&lt;/h2&gt;
&lt;p&gt;

As mentioned before, SQLite stores an entire database, holding one or more tables and all the associated housekeeping, in a single file. So you can just copy that file to another computer, email it to someone, or use standard backup mechanisms to back it up. (Ok, it seems that backing up the database file while changes are being made is not 100% bullet proof, but it&apos;s close.)
&lt;p&gt;

The SQLite3 file format has been backward compatible since 2004, and it&apos;s 32/64-bit and big-endian/little-endian agnostic. This means you can just email a copy of a SQLite database to someone else, and they will be able to use it without trouble. And you can be fairly confident that several decades from now, it&apos;s still possible to retrieve data from an SQLite file.
&lt;p&gt;

SQLite has some interesting Unicode caveats, but I&apos;ll save those for a later post.</description>
  <link>http://www.iljitsch.com/2023/09-04-sqlite-add-a-powerful-database-engine-to-any-app.html</link>
  <guid isPermaLink="true">http://www.iljitsch.com/2023/09-04-sqlite-add-a-powerful-database-engine-to-any-app.html</guid>
  <pubDate>Mon, 04 Sep 2023 14:51:39 GMT</pubDate>
</item>

<item>
  <title>My BGP minilab</title>
  <description>When I wrote &lt;a href=&quot;https://www.oreilly.com/library/view/bgp/9780596002541/&quot;&gt;my first BGP book&lt;/a&gt;
I painstakingly made the config examples on actual Cisco routers. In my opinion, it&apos;s crucial
to make sure that configuration examples that go in a book actually work.
&lt;p&gt;

So when I started writing &lt;a href=&quot;https://www.inet6consult.com/bgpbook/&quot;&gt;my new BGP book&lt;/a&gt;, I did the same. But
this time, I used open source routing software (&lt;a href=&quot;https://frrouting.org/&quot;&gt;FRRouting&lt;/a&gt;)
running in &lt;a href=&quot;https://www.docker.com/&quot;&gt;Docker&lt;/a&gt; containers.
Basically, those &lt;a href=&quot;https://en.wikipedia.org/wiki/OS-level_virtualization&quot;&gt;containers&lt;/a&gt;
are very light-weight virtual machines.
&lt;p&gt;

This makes it possible to run a dozen virtual routers that start up and shut down in just a few seconds.
So it&apos;s very easy to run different examples by starting the required virtual routers with the
configuration for that example.
&lt;p&gt;

This was super useful when I was &lt;em&gt;writing&lt;/em&gt; the book.
&lt;p&gt;

So I thought it would also be very useful for people &lt;em&gt;reading&lt;/em&gt; the book.
&lt;p&gt;

So I&apos;m making the &quot;BGP minilab&quot; with all the config examples from the book available to my readers.
Download version 2022-11 of the minilab that goes with the first version of the book &lt;a href=&quot;2022-11/bgpminilab.zip&quot;&gt;here&lt;/a&gt;.
&lt;p&gt;

You can also run the examples in the minilab if you don&apos;t have the book.
And you can create your own labs based on these scripts.
&lt;p&gt;

The minilab consist of four scripts:
&lt;p&gt;

&lt;ul&gt;
&lt;li&gt;start: to start an example or lab
&lt;li&gt;connectrouter: connect to an already running virtual router
&lt;li&gt;stoprouters: to stop all running routers
&lt;li&gt;run-gortr: runs the &lt;a href=&quot;https://hub.docker.com/r/cloudflare/gortr&quot;&gt;GoRTR&lt;/a&gt; RPKI cache
&lt;/ul&gt;
&lt;p&gt;

There are Mac/Linux shell script and Windows Powershell versions of each script.</description>
  <link>https://www.inet6consult.com/bgpminilab/</link>
  <guid isPermaLink="true">https://www.inet6consult.com/bgpminilab/</guid>
  <pubDate>Fri, 11 Nov 2022 12:15:12 GMT</pubDate>
</item>

</channel>
</rss>
