I'm writing this post for two reasons: 1) To preserve the details of the steps I took to get this all working, and also 2) To help others who might be looking to do the same thing. While I'm not a complete Linux noob, some simple things still are not clear to me, and in other cases, there is conflicting information available on getting DD-WRT running with USB and Samba, so I'm clarifying here what worked for me.
Background
I've had a Windows server running over the years for various reasons. As of late, it's just been a glorified file server. With the proliferation of network storage devices, I figured I could do without running an entire machine all the time. NAS devices are still fairly pricey, though, and I'm aware that some routers now have USB support for external drives. I've been running DD-WRT on my Linksys router for several years now, and I had heard that was some basic support for USB devices in it, so I decided to give it a whirl.
After looking at a combination of factors, including price, USB support, and DD-WRT support, I settled on the Linksys WRT-350N. My goals for this effort were to:
- Run my router on DD-WRT
- Attach a USB drive for storage
- Use Samba to share the storage with other devices on the network
- If possible, spin up a uPNP server for consuming media from an Xbox 360.
Process
Flash your router with DD-WRT
First of all, I installed DD-WRT on the Linksys 350N. I won't go into specifics here, as this is well-documented on the DD-WRT website. The only advice I can give is to always flash your router with the mini build and then upgrade to a standard or mega build.
Advice: The mega build will fit on the 350N, but I decided to go with the standard because of the memory footprint of the mega. Having the standard build gives more room for JFFS, which is needed for the USB storage. I didn't need OpenVPN or Milkfish, so standard worked fine for me. You can see the feature differences here.
After flashing your router, you'll probably want to ensure telnet access is enabled. To make editing files easier, I also enable SSH and use SCP to transfer/edit files on the router.
Configure DD-WRT for USB
For the most part, I followed the instructions from the website. First off, you'll need to enable jffs (details in USB instructions). Next, you want to update the ipkg that comes in DD-WRT so that you can grab some packages needed for USB support. (Note: I needed the OHCI driver for my USB setup).
1: ipkg update
2:
3: ipkg -force-depends install kmod-usb-core kmod-usb2 kmod-usb-storage kmod-usb-ohci
4:
I used the ext3 filesystem on my attached storage, so I also needed ext3 support:
1: ipkg install kmod-ext3
If you need xfs, ext2, or fat support, you'll need to install those modules as well. See the USB install link above. Package names are: kmod-vfat, kmod-ext2, and kmod-xfs.
Get USB drive ready
This might be a tad out of order, but you'll also need a USB drive to be used with the router. I pulled one of the 60GB SATA drives out of my old server and am using a SATA to USB 2.0 drive enclosure. Because I knew in advance I'd be using ext3 filesystem on the router, I went ahead and partitioned/formatted my drive using an Ubuntu LiveCD. You can download just about any Linux distro -- I was familiar with Ubuntu and I knew the LiveCD comes with a partition editor, so I just used that. Again, I won't go into using a partition editor within your Linux distribution, as that is documented well elsewhere.
What I will say is that it's useful to have three partitions on your drive: 1) Space for Optware packages (more on this later), 2) general "data" space (what I am using to share files over the network), and 3) some swap space (format as linux-swap). I split them up as: 1) Optware (1GB), 2) Data (58GB), and 3) Swap (1GB).
I plugged in my USB drive to the router. At this point, the router isn't running any drivers that recognize the drive. We'll remedy that next.
Loading USB Drivers
Next, we need to load the drivers we just installed to be able to see the attached USB drive. You can do this manually, but you'll want it to happen automatically when the router boots. Keep in mind that the embedded Linux firmwares are largely read-only. Enabling jffs allows to use a small portion of remaining free flash ROM as read/write area.
DD-WRT scans a few, well-known paths for startup scripts when it boots. We'll use the one in /jffs/etc/config. Create this folder is it doesn't already exist, either with a telnet connection or WinSCP.
My startup file is named init.startup and is shown as follows. Note that the looping functionality is because, while testing, my USB drive wasn't always recognized immediately and appropriately, and so we loop to attempt to re-init the drivers. Since I have disabled the EHCI driver (which is responsible for USB 2.0 compatibility, if I'm not mistaken), this is much more reliable. More work on this driver is needed, I think. USB 1.1 speeds are OK for my scenario. Also note that apparently under certain circumstances, your startup script can be executed again, well after the router has booted. So that we don't mess anything up then, we do an initial check to see if we're just booting, and only run then. Note also that the entire script is wrapped in a pipe out to /tmp/init.log so we can see any errors upon startup. /tmp is another read/write path within DD-WRT.
1: #/bin/sh
2: (
3:
4: echo "Running init.startup."
5:
6: # Only run once. Check time and exit if running after startup.
7: # Get uptime and round it to nearest integer
8: uptime=`cat /proc/uptime | awk -F\. '{print $1}'`
9:
10: if [ $uptime -gt 120 ]
11: then
12: echo "Did not run init.startup because uptime is $uptime."
13: exit
14: fi
15:
16: # Init USB support and map drives
17: # Loop until drive is detected
18: while [ 1 -eq 1 ]
19: do
20: insmod /jffs/lib/modules/2.4.30/usbcore.o
21: #insmod /jffs/lib/modules/2.4.30/ehci-hcd.o
22: insmod /jffs/lib/modules/2.4.30/usb-ohci.o
23: insmod /jffs/lib/modules/2.4.30/scsi_mod.o
24: insmod /jffs/lib/modules/2.4.30/usb-storage.o
25: insmod /jffs/lib/modules/2.4.30/sd_mod.o
26:
27: sleep 5
28:
29: if dmesg | tail -n10 | grep 'Attached scsi disk' 2>/dev/null 1>/dev/null
30: then
31: break
32: else
33: rmmod sd_mod
34: rmmod usb-storage
35: rmmod scsi_mod
36: rmmod usb-ohci
37: #rmmod ehci-hcd
38: rmmod usbcore
39:
40: fi
41: done
42:
43: # Add other drivers needed for external drives
44: insmod /jffs/lib/modules/2.4.30/jbd.o
45: insmod /jffs/lib/modules/2.4.30/ext3.o
46:
47: echo "DONE"
48:
49: ) > /tmp/init.log 2>&1
You'll need this script to be executable by the system. Make sure you 'chmod 700 init.startup' or use WinSCP to modify the execute bit on the file. Otherwise, it won't run at startup.
Now it's the time to test out our drivers. You can manually type in the insmod command to load the pertinent drivers, or you can reboot to test your script. Either way, after you've got the drivers loaded, you'll want to see if Linux can 'see' your drive. Issue a 'dmesg' command. You should see that a SCSI device was detected, and that it was attached.
1: hub.c: new USB device 00:02.0-1, assigned address 2
2: scsi0 : SCSI emulation for USB Mass Storage devices
3: Vendor: Maxtor 6 Model: Y060M0 Rev:
4: Type: Direct-Access ANSI SCSI revision: 02
5: Attached scsi disk sda at scsi0, channel 0, id 0, lun 0
6: SCSI device sda: 120103200 512-byte hdwr sectors (61493 MB)
7: Partition check:
8: /dev/scsi/host0/bus0/target0/lun0: p1 p2 p3
You're good to go at this point if you see that. With the EHCI driver, I often saw "USB device not accepting new address=x (error=-71)". That's why the loop exists, and why it's no longer being loaded.
Now you can look at the disk and the partitions in a couple different ways:
1: root@gateway:~# ls /dev/discs/disc0
2: disc part1 part2 part3
3: root@gateway:~# ls -l /dev/discs/disc0
4: lr-xr-xr-x 1 root root 31 Jan 1 1970 /dev/discs/disc0 -> ../scsi/host0/bus0/target0/lun0
You can see that there are the three partitions, as well as the /dev/discs/disc0 being an alias for a longer name /dev/scsi/host0/bus0/target0/lun0. Now that we know where the drive is, let's use it by mounting it locally. We want to start with the /opt folder, as by using Optware packages, we can install packages to our heart's content on the first partition (assuming we have enough storage space and device memory to run them, of course).
1: mount -t ext3 /dev/discs/disc0/part1 /opt
2: mount --bind /opt /jffs/opt
This bit of magic mounts our first partition to /opt and rebinds /opt to /jffs/opt, effectively making it writable. Let's mount the data portion to a suitable location. It could be put in any writeable location. I like the concept of putting it on /opt and using that as a mount point.
1: mkdir /opt/nas
2: mount -t ext3 /dev/discs/disc0/part2 /opt/nas
Note: You only have to make the /opt/nas dir once, whereas we'll need to mount the disk every time. That will be covered later when we extend our startup script.
To mount the swap partition, we'll need some more utilities, namely swapon. I tried to use this from a package called swap-utils, but never could get it to work. Instead, I just download busybox and use it from there. If anyone has any suggestions, leave me a comment.
1: ipkg install busybox
2: /opt/bin/busybox/swapon /dev/discs/disc0/part3
Note: You'll only need to install the package once, of course. The call to swapon should be on bootup. We'll see that in the extended startup script later.
At this point, we should have three mounted drives. Check them via the following two commands. 'mount' should show you the mount points, and free should show that there is available disk space.
Installing Optware
Check the DD-WRT site for installing Optware. The documentation there is good. Some notes: I used the 3iii.dk link. Also make sure you 'chmod' the downloaded /tmp/optware-install.sh file. Run the file and it should install the Optware installer.
Installing Samba
You'd think installing Samba would be fairly straightforward. Well, technically, OK, it is. Getting it to run as you expect is not. Most of the documentation assumes you know things you may not, and there are a TON of options to set here. Check the Optware page again for installing Samba. The instructions there for installing are accurate. However, to get things up and running, make sure you take note of the following:
Change the default xinetd allow ports in /opt/etc/xinet.conf. This is usually CIDR format. If you run a typical 192.168.1.1-255 subnet, you should probably set this to:
1: defaults
2: {
3: only_from = 192.168.1.0/24
4: instances = 60
5: log_type = SYSLOG authpriv info
6: log_on_success = HOST PID
7: log_on_failure = HOST
8: cps = 25 30
9: }
For me, the Samba configuration I wanted was an anonymous share for music and pictures. You can use the SWAT web configuration tool, but be careful. You can quickly make a set of incompatible settings. For me, I ended up with this:
1: # Global parameters
2: [global]
3: workgroup = STORAGE
4: netbios name = FILE01
5: server string = Samba File Storage
6: interfaces = 192.168.1.1
7: encrypt passwords = Yes
8: map to guest = Bad User
9: null passwords = Yes
10: lanman auth = No
11: log file = /opt/var/log/samba/log.%m
12: max log size = 25
13: name resolve order = wins hosts lmhosts bcast
14: socket options = TCP_NODELAY SO_RCVBUF=8192 SO_SNDBUF=8192
15: load printers = No
16: wins support = Yes
17: hosts allow = 192.168.1.0/255.255.255.0
18: status = No
19:
20: [media]
21: comment = Media share
22: path = /opt/nas/Media
23: force user = root
24: read only = No
25: inherit permissions = Yes
26: inherit acls = Yes
27: guest only = Yes
28: guest ok = Yes
29: status = Yes
Key features to note are: I map bad user names to the guest user (by default, Windows machines attempt to pass over the current Windows user credentials). For the media share, I set to guest only, guest access is ok, and I force the user (impersonation, basically) to root. This is to create consistency on the owner of the files as they are modified, created, etc. Please appreciate that this is basically giving 'superuser' access to all the files in this folder/subfolders, so make sure this is something you want to do if you copy my configuration.
The other key missing task from the instructions on the DD-WRT website is that, under the hood, the 'guest' user account is an account called 'nobody'. You'll need to create the user account both in the Linux system (you'll have to do this on boot every time -- again in a script), but you also need to create the account for the Samba system. Do this with the smbpasswd command:
1: /opt/bin/smbpasswd -a nobody
I used a blank password when requested.
Start samba using the instructions at the DD-WRT site and you should be good to go. If you map a drive from Windows, or use Start | Run, or use net use, and a user is requested, you should be able to put in anything, as this will be mapped to the guest account.
Sharing Media with uPNP
The ultimate for me is to be able to share my media with uPNP devices on the network (such as my Xbox). I started with uShare, but this only worked with my pictures. It didn't work with my music. I guess it's a basic media server, and can't transform on the fly. Apparently, the Xbox only supports some specific encoding formats, and uShare won't convert mine on the fly to something compatible.
I'm going to look at TwonkyMedia next, but it's a licensed (costs) product, and there is very little documentation on getting it working under DD-WRT. If/when I do, I'll post back here.
Final Thoughts and Startup Scripts
There are some other aesthetic/functional elements to automating all of this at startup. I didn't cover everything, but I will provide my startup scripts here for others. Any questions, ask in the comments.
/jffs/etc/config/init.startup:
1: #/bin/sh
2: (
3:
4: echo "Running init.startup."
5:
6: # Only run once. Check time and exit if running after startup.
7: # Get uptime and round it to nearest integer
8: uptime=`cat /proc/uptime | awk -F\. '{print $1}'`
9:
10: if [ $uptime -gt 120 ]
11: then
12: echo "Did not run init.startup because uptime is $uptime."
13: exit
14: fi
15:
16: # Init USB support and map drives
17: # Loop until drive is detected
18: while [ 1 -eq 1 ]
19: do
20: insmod /jffs/lib/modules/2.4.30/usbcore.o
21: #insmod /jffs/lib/modules/2.4.30/ehci-hcd.o
22: insmod /jffs/lib/modules/2.4.30/usb-ohci.o
23: insmod /jffs/lib/modules/2.4.30/scsi_mod.o
24: insmod /jffs/lib/modules/2.4.30/usb-storage.o
25: insmod /jffs/lib/modules/2.4.30/sd_mod.o
26:
27: sleep 5
28:
29: if dmesg | tail -n10 | grep 'Attached scsi disk' 2>/dev/null 1>/dev/null
30: then
31: break
32: else
33: rmmod sd_mod
34: rmmod usb-storage
35: rmmod scsi_mod
36: rmmod usb-ohci
37: #rmmod ehci-hcd
38: rmmod usbcore
39:
40: fi
41: done
42:
43: # Add other drivers needed for external drives
44: insmod /jffs/lib/modules/2.4.30/jbd.o
45: insmod /jffs/lib/modules/2.4.30/ext3.o
46:
47: echo "Mounting drives and re-binding locations."
48:
49: # Mount /opt
50: mount -t ext3 /dev/discs/disc0/part1 /opt
51: mount --bind /opt /jffs/opt
52:
53: # Mount swap
54: /opt/bin/busybox swapon /dev/discs/disc0/part3
55:
56: # Mount NAS
57: mount -t ext3 /dev/discs/disc0/part2 /opt/nas
58:
59: # Call Optware startup
60: echo "Calling Optware script."
61:
62: /jffs/etc/config/optware.start
63:
64: echo "DONE"
65:
66: ) > /tmp/init.log 2>&1
/jffs/etc/config/optware.start (I don't use the .startup extension because it would be run automatically by the OS, and I want to control when it's run):
1: #/bin/sh
2: (
3:
4: # Optware tasks
5: echo "Executing Optware tasks."
6:
7: unset LD_LIBRARY_PATH
8: unset LD_PRELOAD
9:
10: if [ -e /opt/etc/profile ]; then
11: cp /opt/etc/profile /tmp
12: mount -o bind /tmp/profile /etc/profile
13: fi
14:
15: if [ -d /opt/etc/init.d ]; then
16: for f in /opt/etc/init.d/S* ; do
17: [ -x $f ] && $f start
18: done
19: fi
20:
21: ) > /tmp/optware.log 2>&1
/opt/etc/profile:
1: export PATH=/opt/bin:/opt/sbin:/bin:/sbin:/usr/bin:/usr/sbin
2: export PS1='\u@\h:\w\$ '
3:
4: [ -x /opt/bin/less ] || alias less=more
5: [ -x /otp/bin/vim ] || alias vim=vi
6:
7: arp() { cat /proc/net/arp; }
8: ldd() { LD_TRACE_LOADED_OBJECTS=1 $*; }
9:
10: reboot()
11: {
12: killall nmbd
13: killall smbd
14: sleep 1
15: umount /opt
16: /sbin/reboot
17: }
Acknowledgements
Most all of this knowledge was taken from others who already pioneered their way to getting this to work. My scripts are compilations of scripts from others ... too many to list. Lots of this information was in the DD-WRT forums or wiki site. If your work is largely represented here without acknowledgement, and you would like some, let me know in the comments. And my thanks & apologies in advance. :)