EL7 – Loading a Network Module during Install


Last night I tried to perform a net-install on a system that uses the nvidia forcedeth network module. I quickly discovered that Redhat no longer includes the module in the EL7 kernel. No worries, I would simply compile the module, copy it to flash drive, load it into the kernel, and be on my way. Well, that didn’t go as planned.

After loading the module, I attempted to the install. After selecting my desired language, I was presented with an anaconda python traceback which prevented the installer from proceeding.

Traceback (most recent call first):
  File "/usr/lib64/python2.7/site-packages/pyanaconda/nm.py", line 593, in nm_device_setting_value
    raise SettingNotFoundError(name)

  File "/usr/lib64/python2.7/site-packages/pyanaconda/ui/gui/spokes/network.py", line 236, in __init__
    uuid = nm.nm_device_setting_value(device.get_iface(), "connection", "uid")

The installer appeared to be crashing because there wasn’t a NetworkManager connection for the Ethernet device. A connection can easily be added using nmclient.

$ nmcli connection add type ethernet ifname enp0s10 


Objective: To perform a net-installation, with an unsupported network adapter.

Assumptions: These steps assumes that you have a compiled kernel module for your Ethernet adapter that is compatible with the EL7 kernel shipped with the installer.


  1. copy the network adapter kernel module to a usb stick
  2. boot the net install media
  3. switch from the GUI installer to TTY1 by pressing Ctrl+Alt+F1
  4. Enter the “shell” window in tmux (press ctrl+b 2)
  5. Mount the usb-stick and load the module
    $ mkdir /mnt/usb
    $ mount /dev/sdb1 /mnt/usb
    $ insmod /mnt/usb/module.ko
  6. verify the network Interface was created and note its name
    $ ip link
    1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT 
        link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    2: enp0s10: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT qlen 1000
        link/ether 00:19:66:bf:9a:d8 brd ff:ff:ff:ff:ff:ff
  7. Create a connection in Network Manager
    $ nmcli connection add type ethernet ifname enp0s10 

    Replace enp0s10 with the name of your network interface

  8. Switch back to the GUI Installer by pressing Ctrl+Alt+F6

At this point, you can continue with the installation. If necessary, you can use the installer’s network configuration tool to set a static ip or change other network related settings.

After installation, you should install the package that supports your network adapter.

The elrepo repository provides a number of kernel modules that do not ship with the EL kernel.

building tmux 1.9a statically

At work, I use tmux on daily basis. Unfortunately, the version shipped in epel (extra packages for enterprise linux) is slightly dated, v1.6. I didn’t want to update tmux system wide as this would impact other users. A single statically linked library that I could store in my home area would be perfect.

I discovered the build process was bit of a challenge. Although I found answers to my problems, they were not in a centrally located. Therefore, I am documenting my steps below.

These steps allowed me to build tmux 1.9a. These same steps should work for newer versions, however at some point, these steps may break. Additionally, these steps were performed on RHEL6 – they should apply to nearly any distro.

  1. Install glibc-static:
    $ yum install glibc-static
  2. Download libevent-2.0.21-stable
    $ ./configure --prefix=/tmp/local --disable-shared
    $ make
    $ make install
  3. Download ncurse 5.9
    $ ./configure --prefix=/tmp/local --with-default-terminfo-dir=/usr/share/terminfo  --with-terminfo-dirs="/etc/terminfo:/lib/terminfo:/usr/share/terminfo"
    $ make
    $ make install
  4. Download Tmux Source
    $ ./configure --enable-static CFLAGS="-I/tmp/local/include -I/tmp/local/include/ncurses" LDFLAGS="-L/tmp/local/lib -L/tmp/local/include -L/tmp/local/include/ncurses" LIBEVENT_CFLAGS="-I/tmp/local/include" LIBEVENT_LIBS="-L/tmp/local/lib -levent"
    $ make -j5
    $ cp tmux /home/$USER/bin


Troubleshooting / Build Issues:
Continue reading

Android: Email deletion does not propagate to IMAP server

Problem: When deleting a message in the Email client, the deletion does not propagate to the IMAP server.

Device: Motorola Moto X
OS: Android 4.4
App: Email 4.4-51
Server: CentOS 6.5 – dovecot 2.0.9 (version that ships with centos)

Solution: Upgrade dovecot from 2.0.9 to 2.2.10

There has been an open android bug (1029) regarding this issue since 2008.

Comment 86 on Android bug 1029 provides a possible explanation for what is happening:

“I believe the root cause is that the route Google and others have gone with IMAP is to try and make it perform like Exchange, where it stays connected and “pushes” emails immediately when they arrive. The big problem with this is that while IMAP was designed to work this way, it assumed
1. that there would be only one client connected to an account at a time, and
2. the internet connection and IP address of connected clients would be constant, not changing as you move between wifi hotspots and cell towers.
So, most mail servers queue up the “delete” commands until the user has disconnected. If we disable the ill-conceived “PUSH” crap, the mail program SHOULD operate like POP – connect, sync, disconnect. But either it is failing to disconnect, or a second connection (such as a home desktop you left logged in) is keeping your account open. K-9 seems to deal with this better by “remembering” what messages have been deleted and not restore them when it sees that they are still on the server.


Meanwhile, you can make the problem *somewhat* better if you set your IMAP mail to check less often than the timeout on the server (usually 30 minutes). That way, the hanging connection times out between mail checks, and the server will perform the deletions you’ve asked for.”

When reviewing the dovecot changelog, I did not see any changes that seemed related to this problem. I suspect (I have no real knowledge of IMAP and/or dovecot) that dovecot has changed the way it handles delete commands and thus the problem has been worked around. If anyone has information about what changed in dovecot please share!

Sadly, my solution only works if you can update or switch your mail server software.

ATrpms provides dovecot-2.2.10 for Centos6: http://packages.atrpms.net/dist/el6/dovecot/

Configuring a Router with Comcast Business Class

For the last two months, I used NAT to make use of my 5 static ips. I wanted to do away with NAT and create another network for public ip space. My goal was simple; a router that was attached to two networks: a RFC1918 LAN and a LAN for my public ip block; however this would not be an easy task.

Desired Network Topology:

                       /-------------------\          ____Host A_____
                       +        LAN1       + ------> | |
                       +  +         ----------------
+-----------------+          +--------+          /--------------------\
|      modem      | --> eth0 | router | eth2 --> + LAN2 ( +
|    |          +--------+          \--------------------/
| |


Ideally, the modem would be configured as bridge, with a default route via eth0 to comcast’s gateway. The routing should be simple. Sadly, comcast made this way more difficult.

The modem, a SMCD3G (cable gateway), is a switch and router with modem functionality. In theory, it should be possible to disable the router/switch functionality and turn it into a bridge, however comcast refuses to provision the modems in this manner.

The modem has two interfaces, a coax interface and switched virtual interface (svi). The coax interface is provisioned automatically and is used to communicate with comcast’s gateway. The svi is configured with two network blocks: (RFC1918) and (static ip block). Unfortunately, it is not possible to see how the modem is configured, therefore the above is an educated guess, based on the network traffic I saw from the modem.

The modem’s configuration posed, a slight problem. The modem was configured with, but I wanted to assign to the router. After some trial and error, I figured out how to make this configuration work. The secret: proxy arp.

General Configuration / Theory

Disable the firewall/router functionality on the modem. The modem configuration can be accessed via; the username is ‘cusadmin’ and the password is ‘highspeed’. Comcast follows similar steps when you request them to bridge your modem.
Note, this configuration will not disable the routing and switching functionality of the modem.

Under …

Firewall -> Firewall Options
  * [checked] Disable Firewall for True Static IP Subnet Only
  * [checked] Disable Gateway Smart Packet Detection
  * [unchecked] Disable Ping on WAN Interface

Firewall -> Port Configuration
  * [checked]  Disable all Port Forwarding rules

Firewall -> 1-to-1 Network Address Translation
  * [checked] Disable all

LAN -> IP Setup
  * [unchecked] Enable LAN DHCP

Insure there are no static routes configured on the modem; filtering is disabled; a dmz has not been configured; and website blocking is disabled.

Assign to router:eth1 (lan1) and assign (lan2) to router:eth2

To get traffic between the modem and router, router:eth0 needs to be configured. Assign to router:eth0 and set the default gateway to (the modem’s lan ip).

At this point, it should be possible to access the internet from the router. The src ip of packets going out eth0, will be Since is not routeable on the internet, the modem will SNAT to To fix this we need set the src ip of the default route, to match the ip that is configured on router:eth1 (

Example: ip route add default via src

A tool such as ifconfig.me will show the external src ip of the host.
From your host run: curl http://ifconfig.me lives on the modem, outside of LAN1, therefore a static route to via (modem) should be configured.
ip route add via
Note: overlapping network space is a poor configuration practice and should be avoided. In this case, it is the only way to configure the network.

The problem

Host A ( tries to ping Echo requests go out, but Host A never receives echo replies. A tcpdump of router:eth0, verifies the icmp traffic is leaving the router with the correct src/dst ips, but does not show any return/inbound traffic, except for the modem. From the tcpdump, one will see the modem is sending the following arp requests:

ARP, Request who-has tell, length 28
ARP, Reply is-at c4:39:3a:02:02:02 (oui Unknown), length 46
ARP, Request who-has (Broadcast) tell, length 46
ARP, Request who-has (Broadcast) tell, length 46
ARP, Request who-has (Broadcast) tell, length 46
ARP, Request who-has (Broadcast) tell, length 46
ARP, Request who-has (Broadcast) tell, length 46

The router can reply to the request for and The router can not reply for addresses not configured. No other devices will respond to these arp requests, because the router and modem are the only devices on the layer 2 segment. Why does this happen? The modem’s svi has an ip address of The network is directly attached and is the final destination. Luckily, we can make use of proxy arp. Proxy arp will make the router respond to all arp requests.

Enable proxy arp on router:eth0.

Now, when HOST A ( pings icmp echo requests are sent and echo replies are received.

SNAT needs to be configured so hosts in LAN2 ( can communicate on the internet. Keep in mind, the address used for SNAT can’t be the router gateway (, modem gateway (, or any host addresses in LAN1.

Applying the above configuration to a Linux router

Make sure the modem has been configured appropriately (see above).

Router Configuration

# set ip addresses
router$ ip addr add dev eth0
router$ ip addr add dev eth1
router$ ip addr add dev eth2

# bring up interfaces
router$ ip link set eth0 up
router$ ip link set eth1 up
router$ ip link set eth2 up

# add routes
ip route add default via src # change src to the ip on eth1
ip route add via # route to the modem's IP

# enable proxy arp on eth0
sysctl net.ipv4.conf.eth0.proxy_arp=1

iptables -A POSTROUTING -s -o eth0 -j SNAT --to-source

pidgin: disconnect specific accounts from cron

At the end of the day, I needed a way to automatically disconnect from my personal instant messaging accounts (jabber, aim), while staying connected to my work jabber server. This could be done manually ( Accounts -> Manage Accounts -> Uncheck $AccountName), however I tend to be extremely forgetful and often stay logged in.

With the aid of python, I wrote a script that will disconnect specific libpurple/pidgin accounts. Modify the accounts list in the script to reflect that accounts you want to disconnect.

This script was a bit tricky, because purple-remote uses dbus to communicate with pidgin/libpurple. Since cron sets up a bare minimum environment, we need to detect the active dbus session and set the following dbus environmental variables: DBUS_SESSION_BUS_ADDRESS, DBUS_SESSION_BUS_PID, DBUS_SESSION_BUS_WINDOWID. With these environment variable set, we identify the account id for each account you specified, and then tell libpurple to disable that account.


import subprocess
import sys
import os
import re

accounts=[{'name':'pyther24', 'protocol':'prpl-aim' },
          {'name':'matthew@pyther.net/Work', 'protocol':'prpl-jabber'},
          {'name':'pyther24@gmail.com/Work', 'protocol':'prpl-jabber'}]

def check_pid(pid):
    """ Check For the existence of a unix pid. """
        os.kill(pid, 0)
    except OSError:
        return False
        return True

def main():
    if len(sys.argv) > 1 and sys.argv[1] == "login":

    # We need to get the dbus environment variable for purple-remote to work
    # These environment variables are stored in $HOME/.dbus/session-bus
    # there may be old sessions stored in .dbus/session-bus, we need to determine
    # which session to use

    env = os.environ.copy()
    # Fetch all sessions in $HOME/.dbus/session-bus
    for filename in os.listdir(dbus_dir):
        filepath = os.path.join(dbus_dir,filename)
        with open(filepath, 'r') as f:
            for line in f.readlines():
                if re.match('^#',line):

    # Determine which dbus session to use
    # Check pid to see if it is alive
    # If pid is alive see if the name contains dbus-daemon
    for s in dbus_sessions:

        if os.path.exists('/proc/{0}'.format(pid)):
            with open('/proc/{0}/cmdline'.format(pid)) as f:
                cmdline = f.readline().strip()
            # DBUS process must be dead

        if re.search('dbus-daemon',cmdline):

    # Get account ids for each account define above
    # Disable / Enable each account

    for a in accounts:
        p = subprocess.Popen(['purple-remote',


        # Store ID
            id = int(p.communicate()[0].strip())
        except ValueError:
            print('Could fetch id for account {0}'.format(a['name']))

        # Disable Account
        p = subprocess.call(['purple-remote',

if __name__ == '__main__':

nxt2004 firmware

I needed to obtain the firmware for my KWorld ATSC 115 which uses the nxt2004 demodulator. The kernel module saa7134 requires the nxt2004 firmware to operate correctly. In the past the process to obtain the firmware was easy, but now that avermedia-usa now longer makes available the driver, it was much harder to track down the need firmware file.

The problem:

m2n:~ $ /tmp/get_dvb_firmware nxt2004
--2012-07-06 22:51:22--  http://www.avermedia-usa.com/support/Drivers/AVerTVHD_MCE_A180_Drv_v1.2.2.16.zip
Resolving www.avermedia-usa.com...
Connecting to www.avermedia-usa.com||:80... connected.
HTTP request sent, awaiting response... 404 Not Found
2012-07-06 22:51:22 ERROR 404: Not Found.

wget failed - unable to download firmware at /tmp/get_dvb_firmware line 662.

I figured I could just search for AVerTVHD_MCE_A180_Drv_v1.2.2.16.zip or dvb-fe-nxt2004.fw and be in business. Unfortunately, it was not that easy as it took me over an hour to find the file.

I came across linux-firmware-nonfree_1.11_all.deb which contained nxt2004.fw

Since it was so hard to find the firmware file I decided to mirror it along with the original deb package that I got it from.


To Install:

  1. wget http://pyther.net/files/firmware/nxt2004/dvb-fe-nxt2004.fw
  2. cp dvb-fe-nxt2004.fw /lib/firmware
  3. reload appropriate module or reboot
  4. check out dmesg to confirm firmware was loaded

Hope this has helped someone…

Password Manager

Best practice states that passwords should contain letters (mixed case), numbers, and symbols, should be at least 8 characters in length, and should never be used twice. However, this isn’t very practical! How are you suppose to remember a different password for each site you have an account for?

I have been using 4 different password for my various accounts. This method has been working moderately well, but from a security standpoint, it’s suicide. I wanted to use a random password for each of my accounts. But, how would I ever remember all my passwords? A password manager, of course!

What I needed:

  • Passwords stored in an encrypted file
  • Master password to unlock the encrypted file
  • View passwords from the cli/ssh
  • Include additional information such as Security Questions and Answers
  • Integrated support for Firefox

What I used…

Vim Outliner

Vim Outliner is an outline processor. A screenshot is worth a thousand words.


By default when you save the file it will be a simple tab delimited text file. Vim, however, supports encryption. First, you need to set the encryption method by typing :setlocal cm=blowfish. If you want Blowfish to be the default encryption method for vim add the setlocal command to ~/.vimrc. Next, to encrypt the file, type :X. You will be prompted to set a password. Finally, save the file. When you open the file, you will be prompted for the password. If you fail to enter the right password you will see garbage characters.

Firefox Integration  (Mozilla-gnome-keyring)

Mozilla-gnome-keyring allows Firefox to store passwords and form logins in gnome-keyring. Gnome-keyring is much more secure than the default password manager in Firefox. The mozilla keyring must be unlocked to add / retrieve passwords. You can define how long the keyring should remain unlocked for (never, 15 minutes, 60 minutes, etc…). On my desktop I unlock the keyring for 60 minutes, but on my laptop I only unlock it for 10. When logging into a site, Firefox still prompts to  “Remember the Password”.  If you let Firefox remember the password, the password automatically gets recorded in the keyring.


I have been using this solution for about a month and it has fit my needs perfectly. I updated most of my accounts so they each have an unique password such as Dmqngi8ZoPyO or XGVoBOmd7Gar. Passwords are being stored twice: in the password file and in gnome-keyring. Since mozilla-gnome-keyring takes care of adding the passwords into gnome-keyring when I login to a site, I only have to record/update my passwords in the encrypted text file. In the rare case that I’m not at my computer, and I need a password, I simply ssh into my server and open the password file in vim.

Although the password file and keyring is encrypted it is still subject to a brute force attack. Make sure to use a strong master password, the longer the better. I would suggest at least 20 characters. In 2009, it would take a super computer 1.5hrs to crack an 8 character (alpha only; lower-case) password, but it would take 631 Billion years to crack a 20 character (alpha only; lower-case) password. Remember, as computers advance these times will decrease. And of course, a key logger could compromise the master password nearly instantly.


Modem Power Level Graphing

I wanted to monitor the downstream and upstream power levels on my Scientific-Atlantic modem (provided by my ISP). My intended goal was to keep logs of the power levels and if my modem “froze” then I could go back to the logs and look for any abnormal power levels.

At first, I just logged the power levels to a simple text file. This worked, but the aggressive logging I was doing generated a lot of data. I thought it’d be nice to see how the power levels changed throughout the day.

The following emerged:

Logging Script (python)

#!/usr/bin/env python2

import urllib2
from BeautifulSoup import BeautifulSoup
from lxml import etree
from StringIO import StringIO
import sys
import datetime

URL = ''

def timestamp():
    return now.strftime("%b %d %H:%M:%S")

def error():
    print "Error"

    page = urllib2.urlopen(URL, timeout=10)
    print timestamp() + ": # dBmV (rx), # dBmV (tx)"

soup = BeautifulSoup(page)

# Remove   markups, because they break the XML parser.
f = StringIO(str(soup.findAll('tbody')[0]).replace(' ', ''))

dom = etree.parse(f)
nodes = dom.xpath('//font')
# We take every value we can get, but do not use them, yet
modemvalues = []
for i in xrange(0, len(nodes), 2):
    modemvalues.append((nodes[i].text, nodes[i+1].text.strip()))

rpl = float(modemvalues[5][1].split(' ')[0]) # dBmV
tpl = float(modemvalues[6][1].split(' ')[0]) # dBmV

print timestamp() + ": " + str(rpl) + " dBmV (rx), " + str(tpl) +" dBmV (tx)"

Usage: ./getModemPl.py >> logFile (cron job)


Nov 06 22:30:41: 0.7 dBmV (rx), 35.4 dBmV (tx)
Nov 06 22:31:41: 0.8 dBmV (rx), 35.4 dBmV (tx)
Nov 06 22:32:41: 0.9 dBmV (rx), 35.4 dBmV (tx)
Nov 06 22:33:41: 0.5 dBmV (rx), 35.4 dBmV (tx)
Nov 06 22:34:41: 0.5 dBmV (rx), 35.4 dBmV (tx)
Nov 06 22:35:41: 0.5 dBmV (rx), 35.4 dBmV (tx)
Nov 06 22:36:41: 0.6 dBmV (rx), 35.4 dBmV (tx)
Nov 06 22:37:41: 0.7 dBmV (rx), 35.4 dBmV (tx)
Nov 06 22:38:41: 0.7 dBmV (rx), 35.4 dBmV (tx)
Nov 06 22:39:41: 0.7 dBmV (rx), 35.4 dBmV (tx)
Nov 06 22:40:41: 0.7 dBmV (rx), 35.4 dBmV (tx)
Nov 06 22:41:41: 0.6 dBmV (rx), 35.4 dBmV (tx)
Nov 06 22:42:41: 0.7 dBmV (rx), 35.4 dBmV (tx)

Graph Generator (takes every 5th log entry)


if [[ ! $1 ]]; then
    echo "No File to Parse"



# Takes every sample from log File
#cat $logFile | sed 's/.dBmV.(rx),//' | sed 's/.dBmV.(tx)//' | sed 's/: / /'  | perl -pe 's/(.*?)\s(.*?)\s(.*)/$1-$2-$3/;' > $data

# Takes every 5th sample (we log every minute)
cat $logFile | sed 's/.dBmV.(rx),//' | sed 's/.dBmV.(tx)//' | sed 's/: / /'  | sed -n 'p;n;n;n;n;' | perl -pe 's/(.*?)\s(.*?)\s(.*)/$1-$2-$3/;' > $data

date=$(head -1 $logFile | cut -d' ' -f1,2)
date2=$(echo $date | sed 's/ /-/')


#rm "$data"


cat << EOF | gnuplot
# --- gnuplot ---
# PNG output
#set terminal png enhanced size 1400,900
set terminal pngcairo size 1200,600 enhanced font 'Verdana,10'
set output "$png"

# SVG Output
#set terminal svg enhanced size 1440,900

# Adds White Background to SVG
set object 1 rect from screen 0, 0, 0 to screen 1, 1, 0 behind
set object 1 rect fc  rgb "white"  fillstyle solid 1.0
#set output "$svg"

# Line Sytes
set style line 1 lc rgb "#8b1a0e" pt 1 ps 1 lt 1 lw 2
set style line 2 lc rgb "#5e9c36" pt 6 ps 1 lt 1 lw 2
set style line 12 lc rgb '#808080' lt 0 lw 1

set timefmt "%b-%d-%H:%M:%S"
set grid
#set grid back ls 12
set key left box
set title "$date - Power Levels"

# X Label
set xdata time
set format x "%H:%M"
#set xlabel "Time"

# Y Data
set ylabel "dBmV (rx)"
#set yrange [-2:2]
#set ytics 0.2
set yrange [-1.6:4.4]
set ytics 0.2

# Y2 Data
set y2range [31:37]
set y2label "dBmV (tx)"
set y2tics 0.2

plot "$data" u 1:2 ls 1 axis x1y1 ti "rx" with lines, "$data" u 1:3 ls 2 axis x1y2 t "tx" with lines



  • Usage: ./graphModemPower.bash /path/to/logfile
  • Modify yrange and y2range to scale to appropriate power levels for you



Feel free to use the scripts,  they work for me, and hopefully they work for you. As always, use at your own risk.

Weather Underground Script

Since weather.com is ending their free XOAP service conkyForecast will no longer work at the end of this month.

Therefore I wrote a small script to get the temperature and condition from Weather Underground

# Fetches Weather info from Weather Underground
# Usage: ./wundergound.py zipcode
# International:
#  * Go to http://www.wunderground.com/
#  * Find your city
#  * Click the RSS icon
#  * Station ID is the number that follows /stations/ in the url

# Values are either True or False

import sys
import feedparser

def usage():
    if international:
        print("  ./wunderground.py StationID")
        print("  ./weunderground.py zipcode")

if not len(sys.argv) == 2:


if international:


if not feed.feed:
    # Assume Error


if metric:


print('%s - %s' % (temp, condition))



$ ./wunderground 11201
69.6F - Clear

I’m using this script for my conky config:

${execi 600 /home/pyther/scripts/wunderground 11201}