Tuesday, March 8, 2016

Premature Optimization

Context

This topic has bugged me for most of my career in software development.  In nearly every project I've been involved with, there's always someone on the team who wants to solve all the anticipated performance "problems" well before any such problem has been encountered.  The trip off into the weeds of analysis-paralysis usually starts with some wild, worst-case-scenario supposition that often derives from a fundamental misunderstanding of real-world system demands (actual load) or a fundamentally flawed mental calculation regarding real-world resource capacity (e.g. processing or network speed).

Manifesto

Bottom line: DON'T PREMATURELY OPTIMIZE!!!

In nearly every circumstance I can remember, it would have been WAY easier to just write the single-threaded, memory-hogging, un-cached, poorly organized, klunker of a piece of software, and THEN profile it to see where it really suffered in a real-world load scenario.  Attempting to anticipate which optimizable areas will present the real problems is a sure-fire way to waste 80% of the available development time figuring out how to solve 20% of the contribution to poor performance.  That old 80/20 rule can be turned in your favor so easily by waiting until you're 80% done with the functional bits and then spend 20% as much effort fixing the things that contribute 80% of the "slow."

Demonstration

If you didn't get the point by now, I'd be wasting my time to try to convince you.  Just try suppressing your urge to make that next program/module you write run in 0.1ms until you have enough information to understand that the rest of the system won't notice any difference until your stuff takes more than 15 seconds to complete.

AWS Lambda Java Deployment Maven Build

Summary


This post describes a "recipe" for building a deployment package for an Amazon AWS Lambda function authored in Java.  The current AWS Lambda documentation (links below in the "references" section) suggest using the "shade" plugin with Maven, which produces an "uber-jar", or using Gradle to produce a zip file containing classes and a "lib" subdirectory with dependency jars.  Neither of those approaches is ideal.  "Uber-jars" cause problems with any code that uses reflection, and using Gradle seems to assume you've completely set Maven aside as a build tool.  This "recipe" assumes you want to use Maven, but you'd still like the zip-containing-jars style deployment package instead of an "uber-jar."

Use Maven's Assembly Plugin


This should be at least a good 90%+ head start on configuring Maven to produce an AWS Lambda deployment package with the dependency libraries still in their original jars and your AWS Lambda function code included in package-directory / class-file format.

Create Assmembly Descriptor

Within the Maven project directory structure, create a file named:

        src/assembly/lambda_deployment_package_assembly.xml

...containing the following content...

<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3 http://maven.apache.org/xsd/assembly-1.1.3.xsd">
    <id>lambda_deployment_package_assembly</id>
    <formats>
        <format>zip</format>
    </formats>
    <includeBaseDirectory>false</includeBaseDirectory>
    <fileSets>
        <fileSet>
            <directory>${project.build.directory}/classes</directory>
            <outputDirectory>/</outputDirectory>
            <includes>
                <include>**/*.class</include>
            </includes>
        </fileSet>
        <fileSet>
            <outputDirectory>extras</outputDirectory>
            <includes>
                <include>readme.md</include>
            </includes>
        </fileSet>
    </fileSets>
    <dependencySets>
        <dependencySet>
            <outputDirectory>lib</outputDirectory>
            <useProjectArtifact>false</useProjectArtifact>
        </dependencySet>
    </dependencySets>
</assembly>

Add Plugin Configuration to pom.xml

Note: If this is a multi-module project, with each module producing its own AWS Lambda function deployment package (.zip), then this goes in the sub-module's pom.xml, not the parent.  If you have a lot of AWS Lambda function sub-modules, it might be worth trying to use pluginManagement in the parent pom, but that's outside the scope of this post.

pom.xml (excerpt)

<project>
    <dependencies>
    ...
    </dependencies>
    ...
    <plugins>
        <plugin>
            <!--  Note: Normally, this plugin would be run using
            "mvn compile assembly:single" but the execution
            clause below binds it to the normal 'package' lifecycle phase
            -->
            <artifactId>maven-assembly-plugin</artifactId>
            <configuration>
                <descriptors>
                    <descriptor>src/assembly/lambda_deployment_package_assembly.xml</descriptor>
                </descriptors>
            </configuration>
            <executions>
                <execution>
                    <id>lambda_deployment_package_execution</id>
                    <phase>package</phase>
                    <goals>
                        <goal>single</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
    ...
</project>

References


  • http://docs.aws.amazon.com/lambda/latest/dg/create-deployment-pkg-zip-java.html
  • http://docs.aws.amazon.com/lambda/latest/dg/java-create-jar-pkg-maven-no-ide.html

Wednesday, February 10, 2016

MicroServices are not MuServices

Someone where I work recently decided to edit a script that had variables named microservice_this or microservice_that to uservice_this and uservice_that.

What is a uservice?  alcohol? cigarettes? weed?

I’m curious why he decided to change the variables in the script and make them inconsistent with pretty much ALL other references to microservices in pretty much EVERY other document or code (at least around here).

I’m not a fan of using the science/electronics abbreviation of "micro" applied to the semantics of the "microservice" software architecture strategy.  IMO, it just makes things more confusing.  Also, it isn’t actually an English alphabet “u” that is used to represent "micro" in those other disciplines, it’s a greek lower case Mu – “μ”.  I’d be really surprised if that special character didn’t render the script un-runnable, so, after this guy made his changes, the script is either wrong, or broken.

One of the reasons it bothers me is that there is a distinction between the prefix micro, which has come to have a legitimate alternative meaning of “very small”, and the more technical meaning of the prefix which means millionth.  "Microservices" really aren’t services that are one millionth the size of bigger services.  They’re just smaller, so the secondary meaning of the prefix “micro" makes some sense, but the singular meaning of Mu (μ) DOES NOT!!!

I guess it will all be moot soon when things like AWS Lambda take over the world and we all switch to nano-services. Will some people insist on calling them nservices???


Just for grins, compare these results:

And then, search this page for “uservice”.


Ok… rant over.  Decided to vent here instead of picking a fight with the genius who decided to change the script variable names.

Also, BTW, this isn't an invitation to argue with me.  This is just my opinion on the matter.  I don't really care whether you agree, and I won't publish any boneheaded, "you're wrong because" comments.

One of my new favorite jokes...

Job Interview
Interviewer: What would you say is your biggest weakness?
Job Applicant: Honesty.
Interviewer: I don't think honesty is really a weakness.
Job Applicant: I don't care what the f*&^ you think!

Sunday, January 10, 2016

Making a Small Lure Cover Using a Toothbrush Cover

Not sure why I didn't think of this sooner but it's a fairly simple task to modify one of those cheap toothbrush covers that snaps over just the head of a toothbrush, and turn it into a handy lure cover.  This will be mostly pictures since most people reading already got the concept.  The only modification that seems to be necessary is to glue a little piece of foam into the toothbrush cover to close up the toothbrush-handle-sized" hole to keep the cover from easily falling off the rod tip / lure. 

Total cost to make two of these is about $1.

Materials:
  • Toothbrush Cover
  • 3/16" Craft Foam Sheet

Tools:
  • Scissors
  • Hot Glue Gun

1) Get the toothbrush covers at Wal-Mart, Dollar Tree, or where-ever such things are sold.

2) Heat up a dual-temp glue gun to the "low" heat setting so you don't melt the foam or plastic.


3) For each toothbrush cover, cut a piece of craft foam to a size that will cover the toothbrush handle opening when attached to the inside of the toothbrush cover lid.


4) Hot glue the foam piece into the lid.


5) Insert both the small lure and the rod tip into the modified toothbrush cover.

Tuesday, December 9, 2014

Samsung Monitor with "Black Display" Restored to Functional Status

Summary

This is yet another "missing manual" post describing how to bring a Samsung SA series monitor back from the brink.  My particular monitor is a SA27A350H that I bought a few years back at Costco.  About a week ago, when I connected a MacBook Pro to this monitor, the monitor looked like it was powered on, but was not displaying anything but "black."  The OSD menu was basically useless since the only thing it would allow to be selected is the "SETUP&RESET" menu item.  Within that, only the PC/AV Mode menu item could be selected.  Then once that was selected, the scroll buttons would not do anything.  So for all practical purposes, the firmware in the monitor had bricked itself.

Research

As usual, the process of finding a solution to this problem led to one frustrating bit of incomplete information after another.  There is a YouTube video that rambles on for a while and says nothing more than "update the firmware and that'll fix it." (reference below).  There were also a few forum posts that indicated essentially the same thing. (references below)  However, when I tried that on my Dell Laptop, running Windows 7, the FirmwareUpdater ran for few seconds and then reported "There is no monitor to upgrade." followed by another message that said "Your monitor already has the latest version or does not need to be upgraded, so it does not require upgrade."  That led me to assume that my monitor already had the latest firmware and that the FirmwareUpdater.exe would never help me out of this mess.  I even made a brief attempt to find a way to patch the FirmwareUpdater.exe utility (or one of its associated dll files) using a disassembler (reference below), but that was proving to be a little too challenging without a debug symbol file.

Samsung FAIL

As I mentioned above, the FirmwareUpdater.exe on Windows 7 just gave up and reported what turned out to be misleading messages.  I still don't know if the monitor actually had an older version of the firmware (which definitely had a bug that caused the "black display" issue.)  I do know that the FirmwareUpdater.exe led me to believe the monitor was up to date and that I couldn't just overwrite the firmware.

Solution

There was a mention somewhere in the forum post about trying the firmware update on another computer.  So, after giving up on getting the monitor to work again, and letting a few days pass, it seemed like it might be worthwhile to dig out an old Windows XP netbook I had with a VGA port and try the FirmwareUpdate.exe thing one more time.  That's what did the trick.  I don't know what's different between Windows XP and Windows 7, or maybe it's the difference in the VGA port on my newer Dell laptop and the older MSI netbook, but whatever it is, the FirmwareUpdate.exe utility actually worked when it ran on the XP netbook.  So, I have a functional monitor again!!  Hope this post has something in it that makes the difference for someone else.  Good luck!!

References


Wednesday, October 1, 2014

OpenVPN - Windows Client with Raspberry Pi Server on Arch Linux

Overview

By the title, you should have gathered that this is a very specific case of using OpenVPN. This might be adaptable to variations on either end, but my intent is to lay out all the steps to get OpenVPN working for just this one scenario. If it weren't such a chore to sift through all the "generic" documentation that includes so much extra stuff that doesn't apply to any given scenario, this probably wouldn't even be worthwhile, but sometimes, it helps to just have all the info for a single example in one place... so here goes.

Server Setup

For more generic, more broad instructions on how to set up OpenVPN on ArchLinux, see this page: https://wiki.archlinux.org/index.php/OpenVPN

For these instructions, the hardware and OS are...
  • Platform: Raspberry Pi B+
  • OS: ArchLinux 
    • More specifically, the output from uname -a is:
      • Linux alarmpi 3.12.26-2-ARCH #1 PREEMPT Sun Aug 31 22:36:40 MDT 2014 armv6l GNU/Linux

Installing Server Packages

  • OpenVPN
    • COMMAND: pacman -S openvpn
  • Easy RSA
    • COMMAND: pacman -S easy-rsa

Creating CA, Certificates, and Keys

 Create a CA certificate to sign other certificates, create a server certificate, and create at least one client certificate.  These instructions are already documented somewhat clearly and succinctly here:
https://wiki.archlinux.org/index.php/Create_a_Public_Key_Infrastructure_Using_the_easy-rsa_Scripts

The remainder of these instructions assume the various certificate and key files have been created with these names:
  • ca.crt  - The public certificate for the Certificate Authority
  • dh2048.pem - The diffie-hellman encryption key (which may also be a weaker/faster 1024 bit key)
  • raspiserver.crt - The public certificate signed by the CA for the OpenVPN server on Raspberry Pi
  • raspiserver.key - The private key signed by the CA for the OpenVPN server on Raspberry Pi
  • winclient.crt - The public certificate signed by the CA for the OpenVPN client on Windows
  • winclient.key - The private key signed by the CA for the OpenVPN client on Windows
  • ta.key - The tls (ssl) hash used by both client and server OpenVPN processes

Configuring OpenVPN on the Arch Linux  Server

When the package manager installs OpenVPN, it creates a new directory named /etc/openvpn which will contain a configuration file for each instance of OpenVPN.  This only describes the process for setting up one "server" instance, so there will just be one configuration file in /etc/openvpn named server.conf.  Create it by copying /usr/share/openvpn/examples/server.conf to /etc/openvpn/

Copy the server side certificate and key files to the /etc/openvpn directory (which is where OpenVPN will look for them, by default).  Alternatively, each of the certificate and key files can be kept in another location and referenced in /etc/openvpn/server.conf file using a fully specified file path like /root/openvpnkeys/ca.crt.  However, there may be some file permission issues to sort out if these files are not copied to /etc/openvpn/.

Next, edit /etc/openvpn/server.conf and modify the defaults.
  • REPLACE:  cert server.crt
    • WITH: cert raspiserver.crt
    • REASON: to match the filename specified above
  • REPLACE: key server.key
    • WITH: cert raspiserver.key
    • REASON: to match the filename specified above 
  • REPLACE: dh dh1024.pem
    • WITH: dh dh2048.pem
    • REASON: to match the filename specified above 
  • UNCOMMENT: ;tls-auth ta.key
    • REASON: to make OpenVPN use the ta.key file
 Also, here are a few brief notes on other modifications to /etc/openvpn/server.conf that may be useful.
  • port 1194 - Change this if you'd like to use OpenVPN via a different network port.
  • proto udp - Change this to "proto tcp" if reliability is more important than speed for the VPN.
  • push "route 192.168.10.0 255.255.255.0" - uncomment this line if the primary network on which the Raspberry Pi device / server is connected is the 192.168.10.0 network.  Note: More on this later in the section on iptables rules.
  • user nobody - uncomment this to reduce the potential security risk when/if there are vulnerabilities discovered in the OpenVPN software
  • group nobody - also uncomment this to reduce security exposure

Adding OpenVPN to the System Startup, and Starting/Stopping the Service

OpenVPN isn't anything too different than a normal service using a systemctl based Linux system, except that the service name is qualified with a suffix reference to the config file.  This makes it possible to start more than one OpenVPN instance (e.g. an OpenVPN server and several OpenVPN clients, or several different OpenVPN servers with different access rules or routes).  The following example commands assume the configuration file is called server.conf
  • systemctl enable openvpn@server
  • systemctl start openvpn@server
  • systemctl stop openvpn@server
 Note: If there were another config file named specialaccess.conf, the systemctl commands would be systemctl {command} openvpn@specialaccess

 

Configuring Linux Packet Forwarding and iptables Masquerading Firewall


In most cases, OpenVPN will "own" a virtual network with a range of address from which the it will issue (via DHCP) an ip address and routing information to a client upon connection.  It is not likely that the subnet used by the OpenVPN server and its virtual network device (TUN/TAP adapter) will be the same subnet configured on the wireless or wired hardware network adapter (wlan0, eth0, etc).  In order to transfer packets to and from other networks, the Linux kernel and firewall must be configured for packet forwarding.  This requires the following few steps:
  • Enable the kernel packet forwarding
    • TEMPORARY:  echo 1 > /proc/sys/net/ipv4/ip_forward
    • PERMANENT: edit  /etc/sysctl.d/99-sysctl.conf, add the following line, and reboot:
      • net.ipv4.ip_forward=1
  • Add masquerade packet forwarding rules to iptables
    Note: This example assumes the "real" network adapter on the Raspberry Pi device is wlan0, and the OpenVPN virtual network adapter is tun0. 
    Adjust the input and output adapters in these rules as necessary.
    For more details, see: http://www.revsys.com/writings/quicktips/nat.html
    and/or https://forums.openvpn.net/topic12491.html
    • /sbin/iptables -t nat -A POSTROUTING -o wlan0 -j MASQUERADE
    • /sbin/iptables -A FORWARD -i wlan0 -o tun0 -m state --state RELATED,ESTABLISHED -j ACCEPT
    • /sbin/iptables -A FORWARD -i tun0 -o wlan0 -j ACCEPT
  • After testing that the forwarding/masquerading rules work, add them to /etc/iptables/openvpn.rules (or some other rules file of your choosing) so that they will be applied again whenever iptables is started.
    • If there isn't already an /etc/iptables/iptables.rules file, then
      • iptables-save > /etc/iptables/iptables.rules
    • If the iptables service is not already enabled to start at boot time...
      • systemctl enable iptables

 

Windows OpenVPN Client Setup

 Note: There is more information about this at: https://community.openvpn.net/openvpn/wiki/Easy_Windows_Guide#ClientConfigFiles but the page covers both client and server installation of OpenVPN on Windows, so all of the instructions there do not necessarily apply to the Windows client side setup.
  • Download OpenVPN GUI and install
  • Create the following directory (because the installer apparently doesn't):  
    • c:\Program Files\OpenVPN\config
  • Copy the client side certificate and key files to the c:\Program Files\OpenVPN\config directory (or another directory if desired.  Config will refer to them by full path anyway.).
    Reminder: These files were generated on the Linux server, so use Filezilla, Putty pscp, or a similar tool to transfer them to the Windows machine.
    • ca.crt
    • dh2048.pem
    • winclient.crt
    • winclient.key
    • ta.key
  • Copy the example OpenVPN client.conf file from the Linux server /usr/share/openvpn/examples/client.conf to c:\Program Files\OpenVPN\config and rename it to client.ovpn
    • Reminder: Also use Filezilla, Putty pscp, or a similar tool for this server-to-Windows transfer.
    • Note: Config files for use by the Windows OpenVPN GUI tool must have an extension of .ovpn or OpenVPN GUI won't "see" them.
  • Modify the client.ovpn config file to use the correct server address.
    • e.g. remote my.raspberrypi.linuxbox 1194
  • Open the Windows Control Panel "Network and Sharing Center", click on "Change adapter settings", and rename the network adapter with device name "TAP-Windows Adapter v9" from "Local Area Connection (3)" (or whatever it was named) to a simpler name with no spaces, like "openvpntap"
  • Test the client configuration first from a command prompt before trying to connect via OpenVPN GUI because if the connection fails in OpenVPN GUI, there is no way to see the error messages.
  • ALERT: If the server is configured with any routes to "push" down to a connecting client, the OpenVPN GUI MUST BE RUN AS ADMINISTRATOR on Windows systems that require UAC (e.g. Vista, Windows-7).  A possible alternative is to set up OpenVPN GUI to run under a special account that is added to the local "Network Operators" group (The various ways to run programs as admin or using a special user will not be covered here since that would be a bit outside the scope of this post, and it's documented plenty of places already... for instance: HERE, or HERE).  
    • Note: The error message that occurs if OpenVPN GUI is _not_ run with admin rights, and the route-method exe / route-delay 2 directives are _not_ in the client configuration is similar to the following log excerpt:
      • MANAGEMENT: >STATE:1412177588,ADD_ROUTES,,,
        C:\Windows\system32\route.exe ADD 192.168.2.0 MASK 255.255.255.0 10.10.8.5
        ROUTE: route addition failed using CreateIpForwardEntry: Access is denied.   [status=5 if_index=52]
        Route addition via IPAPI failed [adaptive]
        Route addition fallback to route.exe
    • Note: The error message that occurs if OpenVPN GUI is _not_ run with admin rights, and the route-method exe / route-delay 2 directives _are_ in the client configuration is similar to the following log excerpt:
      • MANAGEMENT: >STATE:1412177074,ADD_ROUTES,,,
        C:\Windows\system32\route.exe ADD 192.168.2.0 MASK 255.255.255.0 10.10.8.5
        env_block: add PATH=C:\Windows\System32;C:\WINDOWS;C:\WINDOWS\System32\Wbem
        ERROR: Windows route add command failed: returned error code 1
    • See: http://mugurel.sumanariu.ro/windows/vista-and-win-7-openvpn-route-problem/
  • Connect/Disconnect using OpenVPN GUI.
  • OPTIONAL: Configure OpenVPN to pass SMB traffic to connect network shares, etc.
    • In order to allow SMB packets to pass through the OpenVPN adapter, it is necessary to add a gateway route, so add the following route in the server's OpenVPN configuration, (and don't forget to restart it and be sure the client re-connects after the change):
      • push "route 0.0.0.0 0.0.0.0 vpn_gateway 999"
    • Verify the change by checking for the 0.0.0.0 route on the client with weight 999, which should appear near the top of the output from the "route print" command.  Then check to see if SMB (Windows) shares are accessible via addresses that connect through OpenVPN.
    • See: https://forums.openvpn.net/topic13148.html
  • OPTIONAL: Configure the OpenVPN client to automatically retry a connection without ever giving up (might be desirable for a laptop that should reconnect whenever it is resumed from sleep/suspend state).

OpenVPN Clients for Other Operating Systems


Wednesday, August 21, 2013

Blocking Internet at specific times using Netfilter / iptables on OpenWrt Router

From the title, it should already be obvious that this will apply to a very specific case, but I'm sure there are parts of it that will be helpful in other scenarios too.  As is typical with Linux, the power is there, but the lengthy list of vaguely documented pieces and parts make it a challenge to get all the proverbial planets aligned.

What motivated me to sort all this out was a goal of implementing a reasonably cheap, reasonably reliable way to shut off internet access on the tablets my kids have during certain blocks of time each day.  I found a few things like DansGuardian and MIA (Mom's Internet Access) but they were either too complicated to set up, or didn't work on my hardware, or maybe there was some other reason I passed them by... can't remember now.  I also searched for this kind of step by step tutorial that would cover the major points for using iptables (a.k.a. netfilter) firewall rules in Linux to simply shut off traffic from certain devices.  I didn't find anything, so now I'm writing it...  so here goes...

Hardware

* This will probably work on most any wireless access point / router that has been loaded with OpenWRT, but for me it's all set up on a Netgear WNDR3700 v2.
* The rules are set up using the hardware / MAC address on the wireless tablets, which is something all WiFi capable tablets have, so there's nothing special about the devices that are being blocked.

Firmware

On the NetGear router, the stock firmware has been replaced with OpenWrt Barrier Breaker r34054. The version of OpenWrt that would apply to other wireless access points / routers may be different.  See the references below for a link to the OpenWrt compatibility page.  Getting OpenWrt loaded isn't too tough, but I'm not covering that here.

Skills

Most people who get past loading custom firmware on a router will be able to handle the rest of this, but it would help to know your way around an SSH client like PuTTY (ref below), have a basic understanding of networky things like IP addresses, MAC addresses, and understand a little about how timezones and UTC time relate.

Overview

When a wireless device connects to an access point, the network traffic is generally routed (also sometimes called "forwarded") through it to an internet connection.  When the operating system that runs on the access point / router hardware is derived from Linux, as OpenWrt is, there is usually a low level firewall technology built into the core (kernel) of the OS called iptables.  Understanding iptables rules takes a deeper understanding of network protocols and the specifics of packet level data, so there is often a higher level "firewall" product that simplifies how the rules are defined and generates more complicated rules for iptables.  This is also how OpenWrt is set up by default.

Details

iptables "time" module

iptables rules can be defined with -m time --timestart HH:MM:SS --timestop HH:MM:SS to limit the start and end time block within which the rule will be applied.
  • NOTE: The :SS part of the times may be omitted if it is just :00.  Example: --timestart 17:30 --timestop 18:45 is equivalent to --timestart 17:30:00 --timestop 18:45:00
  • WARNING: Somewhere along the way, the default interpretation of HH:MM:SS CHANGED FROM Local-TimeZone TO UTC.  The iptables version distributed with OpenWrt r34054 apparently uses the Local-TimeZone by default.  Documentation on newer versions of iptables says that the default interpretation of HH:MM:SS in iptables is UTC but that may not be the case.  Experiment to see how it actually behaves.
  • WARNING: timestart and timestop cannot cross midnight.  Newer versions of the "time" module for iptables support a --contiguous parameter, but that wasn't supported in the OpenWrt r34054.  If it is supported, it must be specified for a time range like the following to work --timestart 23:00 --timestop 01:00
  • WARNING: If the "Kernel TimeZone" is not in synch with the "Local TimeZone," iptables rules using local times may not behave as you'd expect.  If you have changed the "Local TimeZone" (maybe it was set incorrectly) and you have not rebooted the device (i.e. Linux) since changing it, execute the 'date -k' command to synch things up (or if you're a fan of how M$ Windows works, you could do a full reboot).
  • WARNING: In older versions of iptables, using UTC time requires --utc to be specified in the iptables rule.  The rules operate more clearly based on the UTC time (displayed with 'date -u'), but the tradeoff is that the HH:MM:SS times would not automatically adjust for Daylight Saving Time.
  • WARNING: (...ok this is really a repeat, but...) Older versions of iptables default to LocalTZ, so any documentation referring to the '--localtz' parameter may not apply to an older version of iptables.  Experiment to see how iptables is using the timestart and timestop before concluding that the rule just isn't working.
  • EXAMPLE: 8pm to 7am Mountain Daylight Time (MDT / UTC-6) does not cross midnight in UTC, so it could be specified in one rule:
                  "-m time --utc --timestart 02:00 --timestop 13:00"
  • EXAMPLE: 8pm to 7am Mountain Daylight Time (MDT / UTC-6) does not cross midnight in UTC, so it could be specified in two rules (not sure how you keep packets from squeaking by between 23:59:59 and 00:00, but that one second gap probably won't hurt anything):
                  "-m time --localtz --timestart 20:00 --timestop 23:59:59"
                  "-m time --localtz --timestart 00:00 --timestop 07:00"

iptables "mac" module

Many iptables rule examples you may find use the '-s' parameter to specify a source IP address to match the packets that are to be affected by the rule.  Many access points are set up to use DHCP, which automatically assigns an IP address from a pool of available addresses.  Unless special configuration has been added on the A/P / router, to assign a "static DHCP address lease" to a certain device, the device you're trying to block could end up having a different IP address and iptables rules that use '-s' or '--source' would block the wrong device.  Instead of relying on a dynamically assigned IP address, it is preferable to define rules based on the hardware / MAC address of the device, which will not change.

So, "-m mac --mac-source 9A:BC:DE:F0:12:34" would match packets from a specific hardware device with that MAC address.

NOTE: Some portable devices could have more than one MAC address if they had, for instance, separate radios for different speed WiFi connections (like one MAC address for 802.11B and a different MAC address for 802.11N)  Be sure to make a rule for all possibilities.

iptables "comment" module
Documenting what you've done is always a good idea.  There is a way you can build some commentary right into the rule so you can easily find it when you look at the output of 'iptables --list'  The comment module does the work.
-m comment --comment "This rule blocks Suzy's midnight surfing."

Putting it all Together

This example rule blocks a specific device from accessing the internet between 8pm and 7am MDT (UTC-6).

iptables -I forwarding_rule -m comment --comment "Shut off Georgie's tablet at night" -p tcp -m mac --mac-source 9A:BC:DE:F0:12:34 -m time --utc --timestart 02:00 --timestop 13:00 -j zone_wan_REJECT

NOTE: The target "zone_wan_REJECT" is probably specific to OpenWrt.  It may work in a normal iptables setup to simply specify the "REJECT" target.

Adding the Rule(s)

Using the LuCI web based administrative console for OpenWRT, navigate to the "Network" tab, then within that to the "Firewall" tab, then within that to the "Custom Rules" tab.  Enter the rules here as text and click the "Submit" button.  This just saves the new rules but the firewall must still be restarted to pick up the change.

Restarting the Firewall

There may be a way to do this in LuCI, but I never did find a way.  I found it was easier to SSH into the router using PuTTY and run "/etc/init.d/firewall restart"  An SSH/PuTTY session is also a good place to verify the clock. timezone. etc are set up correctly on the A/P / router.  The "date" command will show many of the clock related things of interest.

An Alternative Way to Generate Firewall Rules

OpenWrt has a higher level firewall configuration tool that generates iptables rules using a config file at /etc/config/firewall.  Some may find it more intuitive to define firewall rules this way so here's the reference page if that's what you prefer.  I abandoned defining my firewall rules this way because I couldn't figure out how to pass through much of what I wanted without just declaring it as "option extra" anyway.
* http://wiki.openwrt.org/doc/uci/firewall

References