Mar 202018

If your ISP offers IPv6 and you have Mikrotik router, it would be shame not to make use of it. My setup assumes you get /64 prefix from your ISP (Comcast in my case) via DHCPv6. Also assumed is empty IPv6 configuration.

First I like to disable default neighbor discovery interface. Blasting IPv6 router advertisements on all intefaces is not necessarily a good idea:

/ipv6 nd
set [ find default=yes ] disabled=yes

Next step is to setup DHCP client. Withing a few seconds, you should see the prefix being allocated:

/ipv6 dhcp-client
add add-default-route=yes interface=ether1 pool-name=general-pool6 request=prefix
:delay 5s
Flags: D - dynamic, X - disabled, I - invalid
 #    INTERFACE             STATUS        REQUEST             PREFIX
 0    ether1                bound         prefix              2601:600:9780:ee2c::/64, 3d14h41m41s

At this time I love to allocate address ending with ::1 to the router itself:

/ipv6 address
add address=::1 from-pool=general-pool6 interface=bridge1 advertise=yes

Now it should be possible to ping its address from external computer (in this example address would be 2601:600:9780:ee2c::1). If this doesn’t work, do check if you have link-local addresses. If none are present, reboot the router and they will be regenerated.

With router reachable, it is time to delegate IPv6 prefix to internal machines too. For this purpose, setup RA (router announcement) over the bridge. While default interval settings are just fine, I like to make them a bit shorter (20-60 seconds):

/ipv6 nd
add interface=bridge1 ra-interval=20s-60s

And that’s all. Now your computers behind the router will have direct IPv6 route to the Internet. Do not forget to setup both router firewall and firewall of individual devices. There is no NAT to save your butt here.

PS: Here is the basic IPv6 firewall allowing all connections out while allowing only established back in:

/ipv6 firewall filter
add chain=input action=drop connection-state=invalid comment="Drop (invalid)"
add chain=input action=accept connection-state=established,related comment="Accept (established, related)"
add chain=input action=accept in-interface=ether1 protocol=udp src-port=547 limit=10,20:packet comment="Accept DHCP (10/sec)"
add chain=input action=drop in-interface=ether1 protocol=udp src-port=547 comment="Drop DHCP (>10/sec)"
add chain=input action=accept in-interface=ether1 protocol=icmpv6 limit=10,20:packet comment="Accept external ICMP (10/sec)"
add chain=input action=drop in-interface=ether1 protocol=icmpv6 comment="Drop external ICMP (>10/sec)"
add chain=input action=accept in-interface=!ether1 protocol=icmpv6 comment="Accept internal ICMP"
add chain=input action=drop in-interface=ether1 comment="Drop external"
add chain=input action=reject comment="Reject everything else"
add chain=output action=accept comment="Accept all"
add chain=forward action=drop connection-state=invalid comment="Drop (invalid)"
add chain=forward action=accept connection-state=established,related comment="Accept (established, related)"
add chain=forward action=accept in-interface=ether1 protocol=icmpv6 limit=20,50:packet comment="Accept external ICMP (20/sec)"
add chain=forward action=drop in-interface=ether1 protocol=icmpv6 comment="Drop external ICMP (>20/sec)"
add chain=forward action=accept in-interface=!ether1 comment="Accept internal"
add chain=forward action=accept out-interface=ether1 comment="Accept outgoing"
add chain=forward action=drop in-interface=ether1 comment="Drop external"
add chain=forward action=reject comment="Reject everything else"

Mar 152018

Setting up NTP server is easy. But actually monitoring that server is a bit more difficult. A bare minimum should be getting an e-mail after reboot. However, even that simple step requires a bit of setup.

First you need to install sendmail, its configuration compiler, and a few SASL authentication methods:

# yum install -y sendmail sendmail-cf cyrus-sasl-plain cyrus-sasl-md5

Next step is preparing authentication database (do substitute e-mail and password):

# mkdir -p -m 700 /etc/mail/authinfo
# echo 'AuthInfo: "U:root" "" "P:password"' > /etc/mail/authinfo/mail
# makemap hash /etc/mail/authinfo/mail < /etc/mail/authinfo/mail

The last configuration step is adding the following lines into <code>/etc/mail/</code> just <strong>ABOVE the first <code>MAILER</code> line</strong>:

define(`RELAY_MAILER_ARGS', `TCP $h 587')dnl
define(`ESMTP_MAILER_ARGS', `TCP $h 587')dnl
define(`confAUTH_OPTIONS', `A p')dnl
define(`confCACERT', `/etc/pki/tls/certs/')dnl
FEATURE(`authinfo',`hash -o /etc/mail/authinfo/gmail.db')dnl

With configuration out of the way, we can proceed with "compiling" that new configuration and restarting the daemon:

# make -C /etc/mail
# systemctl start sendmail

Finally, we are ready to test e-mail via command line:

# echo "Subject: Test via sendmail from `hostname`" | sendmail -v

Assuming everything works, the only remaining task is adding cron task (crontab -e):

@reboot  echo -e "Subject: `hostname` status\n\nHost rebooted at `date -R`." | /usr/sbin/sendmail -v

Now every reboot will result in a e-mail message.

Mar 102018

It all started as a joke.

As few of us started using Slack it seemed oddly appropriate that #random channel should have a freshly squeezed random number every day. But there were some complaints about the quality. The first issue arose when 42 was randomly selected a few days in a row and it all went down hill from there culminating in a whole weekend without a random number. Unforgivable!

To replace such flawed human being a simple script was needed. It was clear from the get-go that script would be written in Bash. Not only my favorite but also supported on my personal servers and extremely easy to schedule via crontab.

Albeit single digit number had a previous occurrence, single-person decision was made that two-digit numbers look the best and should be used going forward. Due to the previous issue with number 42, it was also decided such number cannot appear too often. After all, you don’t answer the question of life, the universe, and everything more than once in a blue moon.

Too keep things on a low key, it was necessary to avoid any Slack bot interface. No, the message should always appear to come from a user. After a while chat.postMessage call was discovered enabling just that. This did require a (legacy) token and came at a cost of future extensibility but it also allowed a lot of faking so it all worked out.

In any case, here is the final script:




NUMBER=$(( RANDOM % 89 + 10)) #random number 10-99
if (( $NUMBER == 42 )) ; then NUMBER=$(( RANDOM % 89 + 10)) ; fi  #about 0.01% chance to get 42 second time

TAGLINE=`shuf -n 1 $TAGLINE_FILE | cut -d'*' -f1`

TEXT="Random number of the day is ${NUMBER}.\\n${TAGLINE}"

curl -X POST \
     -H "Authorization: Bearer $TOKEN" \
     -H 'Content-type: application/json; charset=utf-8' \
     --data "{\"channel\":\"$CHANNEL\",\"text\":\"$TEXT\",\"as_user\":\"true\",\"username\":\"$USERNAME\"}" \

PS: No, illusion is not full, as there will be hints this is sent via API and not by human being. However, hints are small enough that not many will note.

Mar 052018

For a while now my QText utility had an elusive issue. I got multiple reports from Korean and Japanese people that text input doesn’t work properly. Unfortunately they often wouldn’t leave e-mail or wouldn’t feed me with more information to understand the issue.

But eventually, one nice Korean gentleman did manage to show the problem by taking video of him taking notes in Notepad and QText side by side. To reproduce it on my side, I installed Korean keyboard and tried to repeat his (English) sequence: EKS CNR ZL.

In Notepad that sequence resulted with “단축키” while my QText caused text to read “단ㅊㅜㄱ키”. Due to my knowledge of the Korean Starcraft scene, I was aware that Korean letters are grouped into blocks. And obviously QText was somehow messing it up.

After a bit of bumbling around, I found the issue was in OnSelectionChanged handler with further analysis showing the SelectionLength property to be the one causing the actual issue:

protected override void OnSelectionChanged(EventArgs e) {
  this.IsSelectionEmpty = (this.SelectionLength == 0);
  if (this.SelectionLength == 0) { this.CaretPosition = this.SelectionStart; }

Next stop was Microsoft’s Reference Source for .NET where took a look into RichTextBox.cs and SelectionLength property only to see the following comment:

// RichTextBox allows the user to select the EOF character,
// but we don't want to include this in the SelectionLength.
// So instead of sending EM_GETSEL, we just obtain the SelectedText and return
// the length of it.

This little innocent note actually pointed toward SelectedText property which does a lot of work internally, including sending EM_STREAMOUT message. This call unfortunately terminates IME entry a bit early and Korean character block boundaries get broken.

Fix I decided on was to ignore EOF issue from the comment and use EM_EXGETSEL message to determine what is the current selection length. Short version of committed code went something like this:

protected override void OnSelectionChanged(EventArgs e) {
  var range = new NativeMethods.CHARRANGE();
  NativeMethods.SendMessage(this.Handle, NativeMethods.EM_EXGETSEL, IntPtr.Zero, ref range);
  this.IsSelectionEmpty = this.IsSelectionEmpty = (range.cpMin == range.cpMax);
  if (this.IsSelectionEmpty) { this.CaretPosition = range.cpMin; }

private class NativeMethods {
  internal const int WM_USER = 0x0400;
  internal const int EM_EXGETSEL = WM_USER + 52;

  internal struct CHARRANGE {
    public int cpMin;
    public int cpMax;

  [DllImport("user32.dll", CharSet = CharSet.Unicode)]
  internal static extern IntPtr SendMessage(IntPtr hWnd, Int32 Msg, IntPtr wParam, ref CHARRANGE lParam);