The Circle kernel is based on a standard openwrt build, using the Atheros AP123/TP-Link TL-MR3420-v2 platform.
The boot flash chip is 8 megabytes (SPI), and contains u-boot, the kernel, the startup root filesystem (read-only), and a read-write jffs2 rootfs overlay partition (which is mounted as an overlay on top of the startup root filesystem – effectively making “/” read-write.
In addition, there’s an embedded “eMMC” flash chip, which appears on the USB bus as “/dev/sda”. The device claims to be 8 gigabytes, but I’ve found that it’s only 1 gigabyte on my particular Circle device (doing a “dd” after the 1 gigabyte mark results in the eMMC chip “resetting” and unplugging/replugging on the USB bus.
Here’s the kernel startup messages showing the SPI flash partitions:
[ 0.700000] 5 tp-link partitions found on MTD device spi0.0 [ 0.700000] Creating 5 MTD partitions on "spi0.0": [ 0.710000] 0x000000000000-0x000000020000 : "u-boot" [ 0.720000] 0x000000020000-0x00000012e5fc : "kernel" [ 0.720000] mtd: partition "kernel" must either start or end on erase block boundary or be smaller than an erase block -- forcing read-only [ 0.740000] 0x00000012e5fc-0x0000007f0000 : "rootfs" [ 0.740000] mtd: partition "rootfs" must either start or end on erase block boundary or be smaller than an erase block -- forcing read-only [ 0.760000] mtd: device 2 (rootfs) set to be root filesystem [ 0.760000] 1 squashfs-split partitions found on MTD device rootfs [ 0.770000] 0x0000004d0000-0x0000007f0000 : "rootfs_data" [ 0.780000] 0x0000007f0000-0x000000800000 : "art" [ 0.780000] 0x000000020000-0x0000007f0000 : "firmware"
“rootfs” is the read-only startup root filesystem (initramfs, squashfs). “rootfs_data” is the jffs2 read-write overlay. “art” contains Atheros radio configuration data. “firmware” is a pseudo-partition that maps to the entire 8-megabyte flash chip.
Here’s the output of the “mount” command after startup:
rootfs on / type rootfs (rw) /dev/root on /rom type squashfs (ro,relatime) sys on /sys type sysfs (rw,relatime) proc on /proc type proc (rw,noatime) tmpfs on /tmp type tmpfs (rw,nosuid,nodev,noatime) /dev/mtdblock3 on /overlay type jffs2 (rw,noatime) overlayfs:/overlay on / type overlayfs (rw,noatime,lowerdir=/,upperdir=/overlay) tmpfs on /dev type tmpfs (rw,relatime,size=512k,mode=755) devpts on /dev/pts type devpts (rw,relatime,mode=600) debugfs on /sys/kernel/debug type debugfs (rw,noatime) /dev/sda3 on /mnt type ext4 (rw,noatime,nodiratime)
As you can see, “/dev/sda3” (the eMMC flash) is mounted on “/mnt” (ext4). Not shown above: “/dev/sda1” is a swap filesystem, and “/dev/sda2” is also ext4 – seems to be used to store a firmware image for a firmware recovery mode (in case of problems in the future).
Modifying /etc/passwd and /etc/shadow:
As I said, the jffs2 “rootfs_data” partition is mounted as an overlay onto “/”. If you want to modify (for example) the “/etc/passwd” and “/etc/shadow” files, you can TFTP-boot an openwrt kernel (configured for the TP-Link TL-MR3420-v2), login, mount the jffs2 partition, then create your own “/etc/passwd” and “/etc/shadow”.
NOTE: there is another method (using a modified firmware update) to modifying the passwd/shadow files (doesn’t require serial port or TFTP-booting a custom kernel). I will document that in a separate post later.
Here’s what I did (I added a “dummy” user (with password “dummy”) to the system. WARNING: if you screw-up the /etc/passwd and shadow files, it could cause the Circle device to fail to boot-up. I felt safe doing this because I could always TFTP-boot my openwrt kernel using u-boot (so I could always restore the original passwd/shadow files.
NOTE: I first created “passwd.txt” and “shadow.txt” (starting with the “passwd/shadow” I had found in the main initramfs read-only rootfs), confirmed they were OK, then renamed them to “passwd” and “shadow”:
root@(none):/# cat /proc/mtd dev: size erasesize name mtd0: 00020000 00010000 "u-boot" mtd1: 0010e5fc 00010000 "kernel" mtd2: 006c1a04 00010000 "rootfs" mtd3: 00320000 00010000 "rootfs_data" mtd4: 00010000 00010000 "art" mtd5: 007d0000 00010000 "firmware" root@(none):/# mount -t jffs2 /dev/mtdblock3 /tmp/mnt [ 28.240000] jffs2: notice: (324) jffs2_build_xattr_subsystem: complete building xattr subsystem, 1 of xdatum (1 unchecked, 0 orphan) and 19 of xref (0 dead, 8 orphan) found. root@(none):/# cd /tmp/mnt root@(none):/tmp/mnt# ls etc lib usr root@(none):/tmp/mnt# cd etc root@(none):/tmp/mnt/etc# ls circle config dropbear hosts modules.d uci-defaults # NOTE: I did this "cat > passwd.txt", then pasted my modified data to the file root@(none):/tmp/mnt/etc# cat > passwd.txt root:x:0:0:root:/root:/bin/ash dummy:x:0:0:root:/root:/bin/ash daemon:*:1:1:daemon:/var:/bin/false ftp:*:55:55:ftp:/home/ftp:/bin/false network:*:101:101:network:/var:/bin/false nobody:*:65534:65534:nobody:/var:/bin/false root@(none):/tmp/mnt/etc# # NOTE: I did this "cat > shadow.txt", then pasted my modified data to the file root@(none):/tmp/mnt/etc# cat > shadow.txt root:$1$WSc/KLMf$20eCFNT1y3fDTuClxHHuq0:16578:0:99999:7::: dummy:$1$Iila3Ueg$4.Vzs4XROsrFuq2L1Zkpb.:16578:0:99999:7::: daemon:*:0:0:99999:7::: ftp:*:0:0:99999:7::: network:*:0:0:99999:7::: nobody:*:0:0:99999:7::: root@(none):/tmp/mnt/etc# root@(none):/tmp/mnt/etc# chmod og-r shadow.txt root@(none):/tmp/mnt/etc# chmod a+rw passwd.txt root@(none):/tmp/mnt/etc# ls -la passwd.txt shadow.txt -rw-rw-rw- 1 root root 222 Jan 1 00:01 passwd.txt -rw------- 1 root root 213 Jan 1 00:01 shadow.txt root@(none):/tmp/mnt/etc# mv passwd.txt passwd root@(none):/tmp/mnt/etc# mv shadow.txt shadow root@(none):/tmp/mnt/etc# cd root@(none):~# sync root@(none):~# umount /tmp/mnt root@(none):~# reboot
Starting Circle firmware:
As I said, there’s a read-only root filesystem used for startup, and the eMMC is mounted to “/mnt”. The eMMC contains the majority of the Circle firmware – where all of their applications/etc reside.
Here’s the “/etc/rc.local” (on the read-only root filesystem) showing where they mount the eMMC and start the Circle firmware:
#!/bin/sh
fatalError() {
echo "Fatal Error: $1"
echo "fastblink" > /tmp/blueled
exit
}
#
#Main Entry Point
#
exec 1>/dev/console
exec 2>/dev/console
echo "Starting rc.local"
echo "leds-gpio" > /sys/bus/platform/drivers/leds-gpio/unbind
echo "slowblink" > /tmp/blueled
/usr/bin/ledd
ifconfig eth0 10.123.234.1
#make sure SD card is detected
if [ ! -e /dev/sda ]
then
sleep 5;
if [ ! -e /dev/sda ]
then
fatalError "No SD card detected".
fi
fi
#check whether SD card is partitioned correctly
sdready="no";
[ -e /dev/sda1 ] && [ -e /dev/sda2 ] && [ -e /dev/sda3 ] && sdready="yes"
[ "$sdready" = "yes" ] || fatalError "SD card not partitioned correctly"
#SD card is formatted properly, fsck it and mount it
e2fsck -y /dev/sda3
#sync
mount -t ext4 -o rw,noatime,nodiratime /dev/sda3 /mnt || fatalError "Mounting SD card failed"
failsafe | grep 'failsafe packet received' && {
echo "Entering failsafe mode."
exit;
}
echo "Done with rc.local"
if [ -f /mnt/shares/usr/bin/startcircle ]
then
source /mnt/shares/usr/bin/startcircle
else
fatalError "Error: did not find startcircle file"
fi
And, here’s the “startcircle” script from the eMMC:
#!/bin/sh
#final boot-up script to start circle functionality
DIR=/mnt/shares/usr/bin
export PATH=$PATH:$DIR
echo 3 > /proc/sys/vm/drop_caches
cp $DIR/myreboot /tmp/
#hardcode timezone for now
echo PST8PDT,M3.2.0,M11.1.0 > /etc/TZ
#set date to 2000 so we can detect when we get ntp time
date -s 2015.09.11-00:00
iw dev wlan0 interface add apcli0 type station
iw dev wlan0 interface add ra0 type __ap
export WAN="eth0"
ln -s /sys/class/leds/tp-link\:green\:3g/brightness /tmp/blueled
MAC=`ifconfig apcli0 | awk '/HWaddr/{print $5;exit;}'`
echo "$MAC" > /tmp/MAC;
ETHMAC=`ifconfig eth0 | awk '/HWaddr/{print $5;exit;}'`
ip link set ra0 address $ETHMAC
grep "^8C:E2:DA" /tmp/MAC || {
if [ -s /etc/MAC ] ; then
cp -f /etc/MAC /tmp/MAC;
MAC=`cat /etc/MAC`;
ip link set apcli0 address $MAC
else
echo "8C:E2:DA:F0:F0:01" > /tmp/MAC;
ip link set apcli0 address 8C:E2:DA:F0:F0:01
fi
ip link set ra0 address 8C:E2:DA:F0:F0:00
ifconfig eth0 down
ip link set eth0 address 8C:E2:DA:F0:F0:00
}
#check base firmware files
[ -f $DIR/ledd ] && { diff $DIR/ledd /usr/bin/ledd > /dev/null || { cp -f $DIR/ledd /usr/bin/ledd; chmod +x /usr/bin/ledd; killall ledd; sleep 1; ledd & } }
#the running ledd may be the ledd in the ROM, before JFFS2 was loaded
diff /usr/bin/ledd /rom/usr/bin/ledd > /dev/null || { killall ledd; sleep 1; ledd & }
diff -r $DIR/scripts/circle /etc/circle > /dev/null || { cp -f $DIR/scripts/circle/* /etc/circle/; chmod +x /etc/circle/*; }
#start mycircle AP
ifconfig eth0 0.0.0.0
macid=`awk -F: '{print $5 $6}' /tmp/MAC`
cp -f $DIR/scripts/hostapd.conf /tmp/;
grep "Circle-$macid" /tmp/hostapd.conf > /dev/null || sed -i "s/^ssid=.*/ssid=Circle-$macid/g" /tmp/hostapd.conf
grep "MyCircle-$macid" /tmp/hostapd.conf > /dev/null && sed -i "s/^ssid=.*/ssid=Circle-$macid/g" /tmp/hostapd.conf
mkdir -p /var/lib/misc/
$DIR/scripts/aplist_create.sh
#PAUSE chain in iptables
iptables -N PAUSE
iptables -I FORWARD -j PAUSE
iptables -t raw -N RAWPAUSE
iptables -t raw -A PREROUTING -j RAWPAUSE
#Minecraft Pocket Edition Chain
iptables -t raw -N MCPE
iptables -t raw -A PREROUTING -j MCPE
#misc
ln -s /usr/bin/wget /tmp/
if [ ! -s $DIR/configure.xml ]; then
if [ -s $DIR/configure.xml.backup ]; then
cp $DIR/configure.xml.backup $DIR/configure.xml
else
cp $DIR/configure-default.xml $DIR/configure.xml
fi
fi
ifconfig apcli0 up
iptables -t nat -A PREROUTING -i apcli0 -p udp --dport 53 -j REDIRECT
#start cron job
$DIR/tinycron 3600 $DIR/start_updater.sh
#install missing packages
if [ ! -s /usr/sbin/ipset ]; then
opkg install $DIR/pkgs/libmnl_1.0.3-1_ar71xx.ipk
opkg install $DIR/pkgs/kmod-nfnetlink_3.10.49-1_ar71xx.ipk
opkg install $DIR/pkgs/kmod-ipt-ipset_3.10.49+6.20.1-1_ar71xx.ipk
opkg install $DIR/pkgs/ipset_6.20.1-1_ar71xx.ipk
modprobe ip_set
modprobe ip_set_hash_ip
modprobe ip_set_hash_net
modprobe xt_set
fi
#ipset init
ipset create vpns hash:ip
ipset create minecraft hash:ip
ipset create games hash:ip
ipset create games_net hash:net
while read p ; do
if [ ${p:0:1} != "#" ]; then
ipset add games_net $p;
fi
done < $DIR/scripts/iprange_games.txt
ipset -file $DIR/scripts/tor.save -exist restore
#start services
cp -a $DIR/service /var
runsvdir /var/service >/dev/null 2>/dev/null &
#dhcp client script will start the rest of the processes
echo 0 > /proc/sys/net/ipv4/conf/all/send_redirects
echo 0 > /proc/sys/net/ipv4/conf/apcli0/send_redirects
echo 0 > /proc/sys/net/ipv4/conf/$WAN/send_redirects
echo 1 > /proc/sys/net/ipv4/conf/apcli0/arp_ignore
echo 2 > /proc/sys/net/ipv4/conf/apcli0/arp_announce
echo 1 > /proc/sys/net/ipv4/conf/$WAN/arp_ignore
echo 2 > /proc/sys/net/ipv4/conf/$WAN/arp_announce
echo 8 > /proc/sys/net/ipv4/tcp_retries2
sysctl -w net.ipv4.conf.apcli0.send_redirects=0
sysctl -w net.ipv4.conf.all.send_redirects=0
Finally, here’s the output of “find” showing all of the files on the eMMC /dev/sda3 partition:
. ./shares ./shares/usr ./shares/usr/bin ./shares/usr/bin/sitedbd ./shares/usr/bin/pkgs ./shares/usr/bin/pkgs/libmnl_1.0.3-1_ar71xx.ipk ./shares/usr/bin/pkgs/kmod-ipt-ipset_3.10.49+6.20.1-1_ar71xx.ipk ./shares/usr/bin/pkgs/ipset_6.20.1-1_ar71xx.ipk ./shares/usr/bin/pkgs/kmod-nfnetlink_3.10.49-1_ar71xx.ipk ./shares/usr/bin/dhcpsniff ./shares/usr/bin/startcircle ./shares/usr/bin/tracking ./shares/usr/bin/tracking/go ./shares/usr/bin/configure.xml.backup ./shares/usr/bin/runit ./shares/usr/bin/dnsmasq ./shares/usr/bin/service ./shares/usr/bin/service/sitedbd ./shares/usr/bin/service/sitedbd/run ./shares/usr/bin/service/dhcpsniff ./shares/usr/bin/service/dhcpsniff/run ./shares/usr/bin/service/dhcpsniff/down ./shares/usr/bin/service/dnsmasq ./shares/usr/bin/service/dnsmasq/run ./shares/usr/bin/service/goclient ./shares/usr/bin/service/goclient/run ./shares/usr/bin/service/goclient/down ./shares/usr/bin/service/pingpongd ./shares/usr/bin/service/pingpongd/run ./shares/usr/bin/service/timetracker ./shares/usr/bin/service/timetracker/run ./shares/usr/bin/service/timetracker/down ./shares/usr/bin/service/arp2 ./shares/usr/bin/service/arp2/run ./shares/usr/bin/service/arp2/down ./shares/usr/bin/service/abodedaemon ./shares/usr/bin/service/abodedaemon/run ./shares/usr/bin/service/apid ./shares/usr/bin/service/apid/run ./shares/usr/bin/service/rclient ./shares/usr/bin/service/rclient/run ./shares/usr/bin/service/rclient/down ./shares/usr/bin/service/webd ./shares/usr/bin/service/webd/run ./shares/usr/bin/pingpongd ./shares/usr/bin/timetracker ./shares/usr/bin/category_data ./shares/usr/bin/category_data/circle.db ./shares/usr/bin/arp2 ./shares/usr/bin/msleep ./shares/usr/bin/firmware_updater.sh ./shares/usr/bin/dumpsm ./shares/usr/bin/photos ./shares/usr/bin/oui ./shares/usr/bin/oui/oui.txt ./shares/usr/bin/configure-default.xml ./shares/usr/bin/mdnsd ./shares/usr/bin/wpa_passphrase ./shares/usr/bin/abodedaemon ./shares/usr/bin/start_updater.sh ./shares/usr/bin/notifications.txt ./shares/usr/bin/categories.txt ./shares/usr/bin/configure.xml ./shares/usr/bin/last_api ./shares/usr/bin/ledd ./shares/usr/bin/arpscan ./shares/usr/bin/apid ./shares/usr/bin/mini_httpd.pem ./shares/usr/bin/category_descriptions.txt ./shares/usr/bin/rclient ./shares/usr/bin/myreboot ./shares/usr/bin/preksites ./shares/usr/bin/mdns-scan ./shares/usr/bin/log.xml ./shares/usr/bin/timezones.txt ./shares/usr/bin/scripts ./shares/usr/bin/scripts/update_4G_config.sh ./shares/usr/bin/scripts/aplist_create.sh ./shares/usr/bin/scripts/switch_to_ethernet.sh ./shares/usr/bin/scripts/restart_wifi.sh ./shares/usr/bin/scripts/reboot_circle.sh ./shares/usr/bin/scripts/hostapd.conf ./shares/usr/bin/scripts/tor.save ./shares/usr/bin/scripts/install_firmware.sh ./shares/usr/bin/scripts/reset_circle.sh ./shares/usr/bin/scripts/refresh_hosts.sh ./shares/usr/bin/scripts/circle ./shares/usr/bin/scripts/circle/program-sd.sh ./shares/usr/bin/scripts/circle/battery_changed.sh ./shares/usr/bin/scripts/circle/power_down.sh ./shares/usr/bin/scripts/circle/factory_default.sh ./shares/usr/bin/scripts/circle/killall.sh ./shares/usr/bin/scripts/circle/upgrade_firmware.sh ./shares/usr/bin/scripts/get_hidden_ssid_freq.sh ./shares/usr/bin/scripts/iprange_games.txt ./shares/usr/bin/scripts/gw_ping.sh ./shares/usr/bin/scripts/circle_ap.sh ./shares/usr/bin/scripts/udhcpc.sh ./shares/usr/bin/notifications ./shares/usr/bin/notifications/notifications.nextid ./shares/usr/bin/notifications/notifications.tosend ./shares/usr/bin/webd ./shares/usr/bin/tinycron ./shares/UPDATER_VERSION ./shares/DATABASE_VERSION ./shares/VERSION ./update_firmware.sh ./update_database.sh