Information wants to be free...

OpenVPN Setup for Android

There are probably many ways to do this, but this is what worked for me in the end, after several trials and errors. I ended up making a "standalone" server solution based on running in GNU Screen to avoid messing too much with my existing server.

I started by downloading the EasyRSA scripts to help generating certificates and such. Then ran the following commands:

./easyrsa init-pki
./easyrsa build-ca
./easyrsa build-server-full server
./easyrsa build-client-full client
./easyrsa gen-dh
          

You will have to enter a CA key passphrase and PEM passphrase, keep those for later.

Once the files are created, copy them into a new location where everything will be stored, in my case the "openvpn" directory under my home directory:

mkdir ~/openvpn
cp pki/ca.crt ~/openvpn/
cp pki/dh.pem ~/openvpn/
cp pki/issued/client.crt ~/openvpn/
cp pki/issued/server.crt ~/openvpn/
cp pki/private/ca.key ~/openvpn/
cp pki/private/client.key ~/openvpn/
cp pki/private/server.key ~/openvpn/
          


The OpenVPN server configuration file must be created manually, at ~/openvpn/server.cfg with the following contents:

ca ca.crt
cert server.crt
key server.key
dh dh.pem
dev tun
ifconfig 10.8.0.1 10.8.0.2
tls-server
port 1194
proto udp
comp-lzo
push "redirect-gateway def1 bypass-dhcp"
push "dhcp-option DNS 8.8.8.8"
push "ifconfig 10.8.0.2 10.8.0.1"
mode server
verb 4
client-config-dir ccd
          


Create a new directory "ccd" under the directory structure and create the file ~/openvpn/ccd/client with the following single line:

iroute 10.8.0.0 255.255.255.0
          


To be able to start things easily and open the necessary parts of the firewall a script like this can be used, placed at ~/openvpn/start.sh:

#!/bin/sh
screen -S openvpn -d -m sudo openvpn server.cfg

sudo iptables -A INPUT -p udp --dport 1194 -i eth0 -j ACCEPT
sudo iptables -A INPUT -i tun0 -j ACCEPT
sudo iptables -A FORWARD -i tun0 -j ACCEPT
sudo iptables -A FORWARD -i eth0 -d 10.8.0.0/255.255.255.0 -j ACCEPT
          

This particular server already has iptables setup for NAT and such, so that is not present in this configuration.

Finally, the Android OpenVPN application requires a matching "ovpn" file with the client configuration. I had to make this one by manually looking something like this:

client
dev tun                             
proto udp
remote my.openvpn.server.com 1194
resolv-retry infinite
nobind
persist-key
persist-tun
comp-lzo
verb 3
<cert>
-----BEGIN CERTIFICATE-----
<contents of client.crt file>
-----END CERTIFICATE-----
</cert>
<key>
-----BEGIN ENCRYPTED PRIVATE KEY-----
<contents of client.key file>
-----END ENCRYPTED PRIVATE KEY-----
</key>
<ca>
-----BEGIN CERTIFICATE-----
<contents of ca.crt file>
-----END CERTIFICATE-----
</ca>
          


Topic: Configuration, by Kjetil @ 18/08-2019, Article Link

SSD1306 PBM Viewer

I recently bought a SSD1306 miniature OLED display. This communicates through I2C and can be connected to various boards, like the Raspberry Pi 3 which I have used.

As an initial experiment, I have made a Python script that will load a 2-color PBM image file and display it on the OLED. The image has to be in binary (P4) format and exactly 128x64 which is the same as the resolution. I have re-used the same initialization commands as mentioned here to get up and running quickly.

Enjoy:

#!/usr/bin/python
import smbus

class SSD1306(object):
    def __init__(self, bus=1, address=0x3c):
        self._address = address
        self._bus = smbus.SMBus(bus)

        self._command(0xae) # Display off.

        self._command(0xa8) # Multiplex ratio...
        self._command(0x3f) # ...63
        self._command(0xd3) # Display offset...
        self._command(0x00) # ...0
        self._command(0x40) # Display start line at 0.
        self._command(0xa1) # Segment Re-map with column 127 mapped to SEG0.
        self._command(0xc8) # Remapped mode, scan from COM[N-1] to COM0.
        self._command(0xda) # COM pins hardware configuration...
        self._command(0x32) # ...Alternative and Left/Right
        self._command(0xa4) # Entire display ON.
        self._command(0xa6) # Inverse display mode.
        self._command(0xd5) # Display clock...
        self._command(0x80) # ...No clock divide ratio and max frequency.
        self._command(0x8d) # Charge pump...
        self._command(0x14) # ...Enabled.
        self._command(0x20) # Memory addressing mode...
        self._command(0x20) # ...Horizontal.

        self._command(0xaf) # Display on.
    
    def _command(self, command_byte):
        self._bus.write_byte_data(self._address, 0x00, command_byte)

    def _data(self, data_byte):
        self._bus.write_byte_data(self._address, 0x40, data_byte)

    def reset_cursor(self):
        self._command(0x21) # Column address...
        self._command(0x00) # ...start at 0...
        self._command(0x7f) # ...end at 127.
        self._command(0x22) # Page address...
        self._command(0x00) # ...start at 0...
        self._command(0x07) # ...end at 7.

    def pbm(self, filename):
        fh = open(filename, "r")
        if not fh.readline().startswith("P4"):
            raise Exception("Not a binary PBM image!")
        header = fh.readline()
        while header.startswith("#"):
            header = fh.readline() # Ignore comments.
        if not header.startswith("128 64"):
            raise Exception("Dimensions must be 128x64!")
        data = fh.read()
        fh.close()

        if len(data) != 1024:
            raise Exception("Size of data is not 1024 bytes!")

        self.reset_cursor()
        for row_offset in [0, 128, 256, 384, 512, 640, 768, 896]:
            for column in range(0, 128):
                byte  = ((ord(data[row_offset + (column / 8)      ]) >> (7 - (column % 8))) & 1) << 1
                byte += ((ord(data[row_offset + (column / 8) + 16 ]) >> (7 - (column % 8))) & 1) << 0
                byte += ((ord(data[row_offset + (column / 8) + 32 ]) >> (7 - (column % 8))) & 1) << 3
                byte += ((ord(data[row_offset + (column / 8) + 48 ]) >> (7 - (column % 8))) & 1) << 2
                byte += ((ord(data[row_offset + (column / 8) + 64 ]) >> (7 - (column % 8))) & 1) << 5
                byte += ((ord(data[row_offset + (column / 8) + 80 ]) >> (7 - (column % 8))) & 1) << 4
                byte += ((ord(data[row_offset + (column / 8) + 96 ]) >> (7 - (column % 8))) & 1) << 7
                byte += ((ord(data[row_offset + (column / 8) + 112]) >> (7 - (column % 8))) & 1) << 6
                self._data(byte ^ 0xff)

if __name__ == "__main__":
    import sys

    if len(sys.argv) < 2:
        print "Usage: %s <128x64 binary PBM image>" % (sys.argv[0])
        sys.exit(1)

    display = SSD1306()
    display.pbm(sys.argv[1])
          


And here is how it looks with an image loaded:

SSD1306 in action.


Topic: Scripts and Code, by Kjetil @ 21/07-2019, Article Link

Commodore 64 Game Cheats

I recently tried out the same tricks from my older article about DOS games, but for a Commodore 64 game instead. This time using the VICE emulator for Blagger by Alligata, a game I could never finish as a kid.

I present some of the steps I used to create the "cheat" patch for infinite lives.
1) Start the game (should start with 5 lives) and go into the VICE monitor.
2) Dump the memory and state of the C64 with the "dump" command.
3) Continue the game, and just loose 1 life.
4) Re-enter the monitor and dump the memory and state again with the "dump" command.
5) Convert the dumps to hex and do a diff on them, to see what has changed. It may take a while to find what you are looking for, which may require additional dumps.
Eventually I found this suspicious memory area, where two values were decremented twice:

101c101
< 00000740  20 20 20 20 1e 57 57 1e  57 57 1e 7a 3a 3b 42 43  |    .WW.WW.z:;BC|
---
> 00000740  20 20 20 20 1e 57 57 1e  57 57 1e 7a 3a 3b 44 45  |    .WW.WW.z:;DE|
          

Although this is at address 0x74e and 0x74f in the dump file, the actual memory addresses are 0x6ca and 0x6cb due to the format of the dump.
6) Now, with an address to look at, set a watch point on this in the VICE monitor.
7) Continue the game again and loose another life.
8) The monitor should now stop execution automatically if done correctly. In my case it displayed:

(C:$1102) w $06ca
WATCH: 1  C:$06ca  (Stop on load store)
(C:$1102) x
#1 (Stop on  load 06ca)  076 050
.C:0de6  AE CA 06    LDX $06CA      - A:01 X:44 Y:00 SP:f6 ..-....C  184810550
.C:0de9  CA          DEX            - A:01 X:44 Y:00 SP:f6 ..-....C  184810550
(C:$0de9)
          

This assembly instruction (DEX) means the memory area is being decremented, exactly what we are looking for.
9) After investigation of the assembly code in that area I found two decrement instructions. These could be patched with NOP (No Operation) instructions (6502 machine code 0xEA) from the VICE monitor like so:

(C:$0e0c) > $0de9 ea
(C:$0e0c) > $0dea ea
          

10) Now, when continuing to play the game, any lost life is simply ignored.

To apply a patch like this permanently, do it on the file (in this case a PRG file) instead of directly in memory of course. The code location will be different, so it must be searched for manually.

Topic: Configuration, by Kjetil @ 02/07-2019, Article Link

CDROM Drive Test Report

Here is yet another article on hardware testing. Similar to the one on floppy drives and hard disk drives from earlier. Now for my collection of CDROM (and DVD) drives, to filter out which ones still work and which ones doesn't. I have compiled a complete (LaTeX typesetted) report on the test results here. A total of 14 drives were investigated, where 10 were OK, 3 were untestable and 1 was broken.

There are tests on the following hard drive models in the report:
* Aztech CDA 268-01A
* Aztech CDA 468-02I
* Creative CR-587-B
* Goldstar CRD-8160B
* HP XJ-HD166S
* Matsushita CR-504-B
* Philips CDD3600/51
* Pioneer DR-A12X
* Plextor PX-712A
* Plextor PX-716SA
* Samsung TS-H352
* Sony AD-5260S
* Sony CRX320A
* Toshiba XM-5302B

Collection:

CDROM drive collection


Topic: Mundane, by Kjetil @ 09/06-2019, Article Link

Hard Drive Test Report

Here's another article on old hardware testing, like the one on floppy drives from before. This time I decided to go through my collection of hard drives test those, again to filter out which ones still work and which ones doesn't. Once again, I have compiled a complete (LaTeX typesetted) report on the test results here. A total of 13 drives were investigated, where 10 were OK and 3 were broken.

There are tests on the following hard drive models in the report:
* Quantum ProDrive ELS
* Quantum ProDrive LPS (x2)
* Seagate ST-1144A
* Seagate ST-1239A
* Seagate ST-157A
* Seagate ST-3096A
* Seagate ST-3144A
* Seagate ST-3195A
* Seagate ST-351AX (x2)
* Conner CFA270A
* Conner CP-3104

Collection:

Hard drive collection


Topic: Mundane, by Kjetil @ 01/05-2019, Article Link

Floppy Drive Test Report

I decided to go through my collection of old 3.5" floppy drives and test them all, in order to filter out which ones still work or not. I compiled a complete (LaTeX typesetted) report on the test results here. A total of 20 drives were investigated. In short, 13 were OK, 4 were broken and 3 were untestable.

There are tests on the following floppy drive models in the report:
* Citizen LR102061 A.L.
* JPN ZFDD1.44M
* Mitsubishi MF355C-252M
* NEC FD1138H
* Panasonic JU-256A276P
* Panasonic JU-257-204P
* Panasonic JU-257A604P
* Panasonic JU-257A606P (x2)
* Sony MPF920 (x4)
* Sony Model MPF40W-00
* Sony Model MPF520-1
* TEAC FD-235HF
* TEAC FD-235HG
* TEAC FD-335HF
* Y-E Data YD-702B-6039B (x2)

Collection:

Floppy drive collection


Topic: Mundane, by Kjetil @ 02/04-2019, Article Link

64-Bit Number Conversion Tool

Around 10 years ago I posted about a number conversion tool. Several improvements have made it into the tool in those years, so it's time for an update. The most notable change is the support for 64-bit resolution on the numbers (also when compiled on 32-bit systems). In addition there is support for more operators like and (&), or (|), multiplication (*), division (/) and modulus (%).

Here is the updated code:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <limits.h> /* __WORDSIZE */

static uint64_t power_of_two(uint64_t n)
{
  if (n == 0)
    return 1;
  else
    return 2 * power_of_two(n - 1);
}

static char *convert_to_bin(uint64_t integer)
{
  static char buffer[65]; /* Should not be longer than 64-bits + NULL. */
  int i, first_one;

  buffer[64] = '\0'; /* Always terminate. */

  first_one = 63;
  for (i = 63; i >= 0; i--) {
    if (integer >= power_of_two(i)) {
      buffer[63 - i] = '1';
      if (first_one > (63 - i))
      {
        first_one = (63 - i);
      }
      integer -= power_of_two(i);
    } else {
      buffer[63 - i] = '0';
    }
  }

  return &buffer[first_one];
}

static uint64_t convert_from_bin(char *number)
{
  uint64_t value;
  int n;
  char *p;

  value = 0; 
  n = strlen(number) - 3; /* Also minus "0b". */
  p = &number[2];
  do {
    if (*p == '0') {
      n--;
    } else if (*p == '1') {
      value += power_of_two(n);
      n--;
    }
  } while (*p++ != '\0');

  return value;
}

static uint64_t convert_to_int(char *number)
{
  uint64_t integer;

  if (strncmp(number, "0x", 2) == 0) {
    integer = strtoull(number, NULL, 16);
  } else if (strncmp(number, "0b", 2) == 0) { 
    integer = convert_from_bin(number);
  } else if (strncmp(number, "-", 1) == 0) {
    integer = atoll(number); /* Handle signed integers. */
  } else {
    integer = strtoull(number, NULL, 10);
  }
  
  return integer;
}

static uint64_t biggest_int(uint64_t n1, uint64_t n2, uint64_t n3)
{
  /* NOTE: Does not handle signed integers, so padding will be off. */
  if (n1 > n2 && n1 > n3) {
    return n1;
  } else if (n2 > n3) {
    return n2;
  } else {
    return n3;
  }
}

static void display_int(uint64_t integer, char *prefix, uint64_t biggest)
{
  int pad;

  if (prefix != NULL)
  {
    printf("%s", prefix);
  }

#if (__WORDSIZE == 64)
  pad = snprintf(NULL, 0, "%ld", biggest)
      - snprintf(NULL, 0, "%ld", integer);
#else /* __WORDSIZE == 32 */
  pad = snprintf(NULL, 0, "%lld", biggest)
      - snprintf(NULL, 0, "%lld", integer);
#endif /* __WORDSIZE */
  while (pad-- > 0) {
    printf(" ");
  }

#if (__WORDSIZE == 64)
  printf("%ld ", integer);
#else /* __WORDSIZE == 32 */
  printf("%lld ", integer);
#endif /* __WORDSIZE */

  printf("0x");
#if (__WORDSIZE == 64)
  pad = snprintf(NULL, 0, "%lx", biggest)
      - snprintf(NULL, 0, "%lx", integer);
#else /* __WORDSIZE == 32 */
  pad = snprintf(NULL, 0, "%llx", biggest)
      - snprintf(NULL, 0, "%llx", integer);
#endif /* __WORDSIZE */
  while (pad-- > 0) {
    printf("0");
  }

#if (__WORDSIZE == 64)
  printf("%lx ", integer);
#else /* __WORDSIZE == 32 */
  printf("%llx ", integer);
#endif /* __WORDSIZE */

  printf("0b");
  pad = strlen(convert_to_bin(biggest))
      - strlen(convert_to_bin(integer));
  while (pad-- > 0) {
    printf("0");
  }

  printf("%s\n", convert_to_bin(integer));
}

int main(int argc, char *argv[])
{
  uint64_t n1, n2, result, biggest;
  char *prefix;

  if (argc == 2) { /* Just display the number in different forms. */
    n1 = convert_to_int(argv[1]);
    display_int(n1, NULL, n1);

  } else if (argc == 4) { /* Perform extra operation. */
    n1 = convert_to_int(argv[1]);
    n2 = convert_to_int(argv[3]);
    if (argv[2][0] == '+') {
      result = n1 + n2;
      prefix = " + ";
    } else if (argv[2][0] == '-') {
      result = n1 - n2;
      prefix = " - ";
    } else if (argv[2][0] == '^') {
      result = n1 ^ n2;
      prefix = " ^ ";
    } else if (argv[2][0] == '&') {
      result = n1 & n2;
      prefix = " & ";
    } else if (argv[2][0] == '|') {
      result = n1 | n2;
      prefix = " | ";
    } else if (argv[2][0] == '*') {
      result = n1 * n2;
      prefix = " * ";
    } else if (argv[2][0] == '/') {
      result = n1 / n2;
      prefix = " / ";
    } else if (argv[2][0] == '%') {
      result = n1 % n2;
      prefix = " % ";
    } else {
      fprintf(stderr, "%s: error: invalid operator.\n", argv[0]);
      return -1;
    }
    biggest = biggest_int(n1, n2, result);
    display_int(n1, "   ", biggest);
    display_int(n2, prefix, biggest);
    display_int(result, " = ", biggest);

  } else {
    fprintf(stderr, "Usage: %s <number> [<operator> <number>]\n", argv[0]);
    fprintf(stderr, "  number can be integer decimal, hex (0x) or binary (0b)\n");
    fprintf(stderr, "  valid operators: + - ^ & | * / %%\n");
    return -1;
  }
   
  return 0;
}
          


Topic: Scripts and Code, by Kjetil @ 09/03-2019, Article Link

Battery Leakage Report

I have several old retro PCs lying around. And as explained in detail here, battery leakage is a risk to some of these PCs depending on the type of battery. So I have gone through a total of 13 PCs and removed leaking NiCd and NiMH batteries from 4 of them, which had those. I have compiled a complete report of all the PCs with image collages here.

Information on the following branded PCs can be found in the report (in addition to 4 custom builds):
* Commodore PC 20-III
* Commodore PC 30-III
* IBM PS/ValuePoint 433DX/Si
* Zenith ICL-4434-KT
* NCR System 3330
* Compaq Deskpro 386s/20
* Compaq ProLinea 590
* Brother BCN3386SX/52
* Laser 486DX2-50E

Here is one of the worst cases of damage:

NiCd battery leak


Topic: Mundane, by Kjetil @ 09/02-2019, Article Link

uIP support for GR-SAKURA

I have now managed to get the uIP TCP/IP network stack running on the GR-SAKURA board. I'm using the Renesas PHY driver, but my own heavily modified (and simplified) implementation of the Ethernet driver.

The Renesas Ethernet controller has a "Zero Copy" mechanism that uses DMA transfer on incoming and outgoing packets. This is nice and all, but it comes into conflict with the uIP architecture with a single buffer. So I ended up with the separate single uIP buffer and two separate ethernet buffers instead, doing CPU based (memcpy) copying between them. Even though this is not as efficient, it prevents the need for any modifications to the core uIP source code.

One limitation with this initial implementation is that there is no handling of link up or down. The code expects the Ethernet plug to be plugged in at boot and remain connected.

To start using it, get the code from here and unpack it in the uIP source folder. (The "sakura" directory should be alongside the "unix" directory.) Then run "make" inside the "sakura" directory to create the "uip.bin" binary that may be uploaded the GR-SAKURA board.

I have also forked the original uIP repository and made the necessary changes on GitHub.

Topic: Open Source, by Kjetil @ 01/01-2019, Article Link

Joystick Mouse on GR-SAKURA

Here is another project based on the GR-SAKURA board. It translates joystick movement into mouse movement, so it can be used on a PC. This is done reading three analog inputs from the joystick and forwarding data over an UART using the simple Microsoft Serial Mouse format.

The software is based heavily on the 30JH joystick that I happen to have. This is a 3-axis joystick, and the last axis is used to simulate the button presses.

This command in Linux can be used to get it connected:

sudo inputattach -bare /dev/ttyUSB0
          


Here's a photo of the setup:

GR-SAKURA wth Joystick


Here is the main part of the code, which implements the serial mouse protocol:

#include "uart.h"
#include "adc.h"

/* Raw ADC reference values: */
#define JOYSTICK_POS_MAX 2464
#define JOYSTICK_POS_MIN 2080 /* JOYSTICK_POS_MAX - (128 * 3) */
#define JOYSTICK_CENTER  2048
#define JOYSTICK_NEG_MIN 2016 /* JOYSTICK_NEG_MAX + (128 * 3) */
#define JOYSTICK_NEG_MAX 1632

static unsigned char prev_buttons = 0;

static unsigned char convert_adc_to_move_value(short value)
{
  /* Scaling and manual conversion to two's complement. */
  if (value > JOYSTICK_POS_MIN) {
    if (value > JOYSTICK_POS_MAX) {
      value = JOYSTICK_POS_MAX;
    }
    return (value - (JOYSTICK_POS_MIN + 1)) / 3;

  } else if (value < JOYSTICK_NEG_MIN) {
    if (value < JOYSTICK_NEG_MAX) {
      value = JOYSTICK_NEG_MAX;
    }
    return ((value - (JOYSTICK_NEG_MAX + 1)) / 3) + 128;

  } else {
    return 0; /* Filtered. */
  }
}

static unsigned char convert_adc_to_button_value(short value)
{
  if (value > JOYSTICK_POS_MIN) {
    return 0b10; /* Left button pressed. */

  } else if (value < JOYSTICK_NEG_MIN) {
    return 0b01; /* Right button pressed. */

  } else {
    return 0b00; /* No buttons pressed. */
  }
}

static short invert_adc_value(short value)
{
  if (value > JOYSTICK_CENTER) {
    return JOYSTICK_CENTER - (value - JOYSTICK_CENTER);
  } else {
    return JOYSTICK_CENTER + (JOYSTICK_CENTER - value);
  } 
}

static int mouse_format(short x_move_adc, short y_move_adc,
                        short button_adc, unsigned char *data)
{
  unsigned char x_move, y_move, buttons;

  x_move = convert_adc_to_move_value(x_move_adc);
  y_move = convert_adc_to_move_value(invert_adc_value(y_move_adc));
  buttons = convert_adc_to_button_value(button_adc);

  data[0] = 0b11000000;
  data[1] = 0b10000000;
  data[2] = 0b10000000;

  data[0] |= (buttons << 4);
  data[0] |= ((y_move >> 4) & 0b00001100);
  data[0] |= ((x_move >> 6) & 0b00000011);
  data[1] |= (x_move & 0b00111111);
  data[2] |= (y_move & 0b00111111);

  if (y_move > 0 || x_move > 0 || buttons != prev_buttons) {
    prev_buttons = buttons;
    return 1; /* Activity */
  } else {
    return 0; /* No activity */
  }
}

void mouse_loop(void)
{
  unsigned char data[3];

  while(1) {
    asm("wait");
    if (mouse_format(adc_get(0), adc_get(1), adc_get(2), data)) {
      uart0_send(data, 3);
    }
  }
}
          


Get the complete code under the MIT License here, which requires the RX GCC toolchain to compile.

Topic: Open Source, by Kjetil @ 12/12-2018, Article Link

Older articles

Newer articles