Mildly Infuriating Warning

I love Visual Studio’s code analysis. Quite often it will give quite reasonable advice and save you from getting into a bad habits. But not all advice is made equal. For example, look at CA1805: Do not initialize unnecessarily.

First of all, here is code that triggers it:

Code
int i = 0;

According to documentation “explicitly initializing a field to its default value in a constructor is redundant, adding maintenance costs and potentially degrading performance”. I agree on assignment being redundant. However, is it really degrading performance?

If you check IL code generated for non-default assignment (e.g. value 42), you will see ldc.i4.s 42 in .cctor(). If you remove that assignment, the whole .cctor() is gone thus bringing some credibility to the warning.

However, warning was about the default assignment. If you set variable to 0, in IL code you will see EXACTLY the same code as if you left it without the explicit assignment. Despite what warning says, compiler is smart enough to remove the unnecessary assignments on it’s own and doesn’t need your help. For something that’s part of performance rules, there is a significant lack of any performance impact.

I did check more complicated scenarios and there are some examples where code with this rule violation had some difference in IL compared to “fixed” code. However, in my view, that’s work for compiler to optimize around – not to raise warning about it. Or alternatively, since it might be a performance issue in those cases, just raise warning when it’s an issue and not for everything.


PS: And habit of assigning default values will save your butt in C++ if you are multi-lingual.

Windows Product Key in VirtualBox BIOS

As many who use virtual machines for testing, I often need to reinstall the same. One thing that annoys me when getting Windows reinstalled is the prompt for the product key. Yes, you can bypass it temporarily but you still need to enter it when you go and activate the Windows. My laptop has product key embedded in BIOs. Why cannot I do the same with virtual machine? Well, maybe I can.

Investigation started by looking into where exactly the key is located on physical hardware. It was relatively easy to discover this was in ACPI MSDM table. Microsoft even has instructions on how to create the table. If you look a bit further, you can even find description on the MSDM fields.

Offset Length Name Value
0 4 Signature Always MSDM.
4 4 Length Total length is always 0x55.
8 1 Revision Always 3.
9 1 Checksum Checksum over the whole table.
10 6 OEM ID Anything goes; pad with spaces.
16 8 OEM Table ID Anything goes albeit ASCII text is customary.
24 4 OEM Revision Any number will do but keep it positive (little-endian).
28 4 Creator ID Anything goes; pad with spaces.
32 4 Creator Revision Any number will do but keep it positive (little-endian).
36 4 MSDM Version Always 1.
40 4 Reserved Always 0.
44 4 Data type Always 1.
48 4 Reserved Always 0.
52 4 Key length Always 0x1D.
56 29 Product key Product key with dashes included.

It was relatively easy to figure what goes in which field except for the checksum. I didn’t find which checksum method it uses but realistically there are only two when you have a 8-bit checksum. Either it’s CRC or a simple sum of all fields. Well, it was a sum for this one. No matter what, sum of all fields has to be 0. If all fields except for checksum would count up to 250, the checksum would need to be 6 in order to overflow to 0. Actually a trivial thing to calculate.

With the file generated, it’s easy to add it to the VirtualBox VM.

Terminal
vboxmanage setextradata "VM" "VBoxInternal/Devices/acpi/0/Config/CustomTable" /msdm_table.dat

Now your Windows 10 installation can proceed with the product key pre-entered and only activation pending.


PS: This just automatically fills product key during install. You still need to do the activation. This will NOT work with the pirated key.

PPS: Yes, this is essentially the same thing as adding PID.txt to the installation media. The same result but without media modification.

PPPS: And yes, of course I made a generator.


Product Key:

Resizing Fixed VirtualBox Disk

I like using fixed disks for VirtualBox VMs. It just gives me a warm fuzzy feelings when it comes to stability. However, that comes at a cost of not being able to resize it easily. If you try, you’ll get VBOX_E_NOT_SUPPORTED error.

Terminal
vboxmanage modifyhd Test.vdi --resize 262144
0%...
Progress state: VBOX_E_NOT_SUPPORTED
VBoxManage: error: Failed to resize medium
VBoxManage: error: Resizing to new size 274877906944 is not yet supported
VBoxManage: error: Details: code VBOX_E_NOT_SUPPORTED (0x80bb0009)
VBoxManage: error: Context: "RTEXITCODE handleModifyMedium(HandlerArg*)" at line 816

But that doesn’t meant it’s impossible.

First we stop VM and clone its disk to a new image. This will automatically make it dynamic. And it will take a while.

Terminal
sudo vboxmanage clonemedium "Test.vdi" "Test_new1.vdi"
0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100%
Clone medium created in format 'VDI'. UUID: 96e20ba5-65f6-4248-b42e-ab48683a4cf9

With dynamic disk, we can now resize disk image.

Terminal
sudo vboxmanage modifymedium disk "Test_new1.vdi" --resize 262144
0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100%

But resizing leaves us with dynamic disk which is not what I wanted. So we convert it back to fixed. Will take long time but we again have fixed disk once done.

Terminal
sudo vboxmanage clonemedium "Test_new1.vdi" "Test_new2.vdi" --variant Fixed
0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100%
Clone medium created in format 'VDI'. UUID: 2bc060b6-0637-43f9-8a55-8ac6bc69ea0d

Since we want direct replacement, we need to copy old UID to the new image.

Terminal
UUID=`sudo vboxmanage showmediuminfo "Test.vdi" | egrep '^UUID' | awk '{print $2}'`
sudo vboxmanage internalcommands sethduuid "Test_new2.vdi" $UUID
UUID changed to: 4f046c99-d76a-478f-95cf-f52b07927bd0

And now we can remove intermediate step.

Terminal
rm "Test_new1.vdi"

Then we copy file system properties of old image to the new one (probably not needed but it doesn’t hurt).

Terminal
sudo chmod --reference="Test.vdi" "Test_new2.vdi"
sudo chown --reference="Test.vdi" "Test_new2.vdi"

And finally we replace the old image with the new one.

Terminal
mv "Test_new2.vdi" "Test.vdi"

This whole process will take ages and it will require enough space to copy disk twice. Increasing disk from 128 to 256 GB required 128+128+256 – essentially 4x the disk space. But sometime roundabout way is the only way. :)

Automatic Mangling

Mikrotik routers give you a lot of control over traffic. If you stick with IPv4, it’s possible to nicely subdivide your network and use queues to traffic-shape any user’s bandwidth based on IP address. But things are not as easy when you allow IPv6. Suddenly your simple queues are essentially broken and tracking users becomes quite a lot more difficult.

For exactly that purpose, you have firewall mangle rules. In its simplest form, you just use MAC address as a condition to mark your connection and then you use that connection mark to mark each packet. In Mikrotik’s lingo, that would be something like this:

Mikrotik Terminal
/ip firewall mangle add
chain=forward
src-mac-address=12:34:56:78:90:12
connection-state=new
action=mark-connection
new-connection-mark=some-mark

/ip firewall mangle add
chain=forward
connection-mark=some-mark
action=mark-packet
new-packet-mark=some-mark
passthrough=no

Now you can use this packet mark in a simple queue as a condition instead of the subnet or IP. If you go further and create these mange rules in /ipv6 section as you did for /ip, you have yourself an easy way to track traffic even on per-MAC resolution.

The real challenge then becomes entering all these rules into the router. In order to fill this, it’s best to have a script as executing all these commands manually is really error prone. I like to keep this in a file looking something like this:

Configuration file
12:34:56:78:90:ab user1-mark
23:45:67:89:0a:bc user1-mark
34:56:78:90:ab:cd user2-mark

Using this I don’t necessarily track or limit traffic per device but per user. As long as all user’s MAC addresses result in the same mark, queue doesn’t really care.

I mark all automatic entries with !AUTO! in the comment field. Any time I want to update firewall rules, I make sure to delete the old ones first:

Script fragment
OLD_V4_ENTRIES=`ssh router.home "/ip firewall mangle print without-paging" | grep ";;; !AUTO!" | awk '{print $1}' | xargs | tr ' ' ','`
if [[ "$OLD_V4_ENTRIES" != "" ]]; then
ssh router.home "/ip firewall mangle remove numbers=$OLD_V4_ENTRIES"
fi

OLD_V6_ENTRIES=`ssh router.home "/ipv6 firewall mangle print without-paging" | grep ";;; !AUTO!" | awk '{print $1}' | xargs | tr ' ' ','`
if [[ "$OLD_V6_ENTRIES" != "" ]]; then
ssh router.home "/ipv6 firewall mangle remove numbers=$OLD_V6_ENTRIES"
fi

With old entries gone, we can add new entries using something like this:

Script fragment
cat ~/MAC.dat | grep -v '^#' | while read ENTRY; do
MAC=`echo "$ENTRY" | xargs | cut -sd' ' -f1`
MARK=`echo "$ENTRY" | xargs | cut -sd' ' -f2`
if [[ "$MAC" != "" ]] && [[ "$MARK" != "" ]]; then
echo "$MAC $MARK"
ssh -n router.home "/ip firewall mangle add chain=forward src-mac-address=$MAC connection-state=new action=mark-connection new-connection-mark=$MARK comment=\"!AUTO!\""
ssh -n router.home "/ip firewall mangle add chain=forward connection-mark=$MARK action=mark-packet new-packet-mark=$MARK passthrough=no comment=\"!AUTO!\""
ssh -n router.home "/ipv6 firewall mangle add chain=forward src-mac-address=$MAC connection-state=new action=mark-connection new-connection-mark=$MARK comment=\"!AUTO!\""
ssh -n router.home "/ipv6 firewall mangle add chain=forward connection-mark=$MARK action=mark-packet new-packet-mark=$MARK passthrough=no comment=\"!AUTO!\""
fi
done

Script above will go over the whole file, skipping all the comments, and assuming the first field in line is MAC address and the second is the mark you want to assign it. Then it will just add mangle pair to both IPv4 and the IPv6 firewall rules.

The last step is to force firewall to clear the existing connections so it can mark them with the new marks.

Script fragment
ssh -n router.home "/ip firewall connections remove [find]"
ssh -n router.home "/ipv6 firewall connections remove [find]"

Only thing now is to create simple queue based on each mark. And yes, that can be automated too. :)