<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>technical</title>
  <link rel="alternate" type="text/html" href="http://tumbleweed.org.za/tags/technical"/>
  <link rel="self" type="application/atom+xml" href="http://tumbleweed.org.za/taxonomy/term/152/atom/feed"/>
  <id>http://tumbleweed.org.za/taxonomy/term/152/atom/feed</id>
  <updated>2008-09-19T00:06:06+00:00</updated>
  <entry>
    <title>The joy that is SysRq</title>
    <link rel="alternate" type="text/html" href="http://tumbleweed.org.za/2008/10/06/joy-sysrq" />
    <id>http://tumbleweed.org.za/2008/10/06/joy-sysrq</id>
    <published>2008-10-06T11:31:20+00:00</published>
    <updated>2008-10-09T15:14:48+00:00</updated>
    <author>
      <name>tumbleweed</name>
    </author>
    <category term="linux" />
    <category term="sysadmin" />
    <category term="sysrq" />
    <category term="technical" />
    <category term="ubuntu" />
    <summary type="html"><![CDATA[<p>I&#8217;m constantly surprised when I come across long-time Linux users who don&#8217;t know about SysRq.
The Linux Magic System Request Key Hacks are a magic set of commands that you can get the Linux kernel to follow no matter what&#8217;s going on (unless it has panicked or&nbsp;totally&nbsp;deadlocked).</p>

<p>Why is this useful? Well, there are many situations where you can&#8217;t shut a system down properly, but you need to&nbsp;reboot.&nbsp;Examples:</p>

<ul>
<li>You&#8217;ve had a kernel <span class="caps"><span class="caps">OOPS</span></span>, which is not quite a panic but there could be memory corruption in the kernel, things are getting pretty weird, and quite honestly you don&#8217;t want to be running in that condition for any longer&nbsp;than&nbsp;necessary.</li>
<li>You have reason to believe it won&#8217;t be able to shut&nbsp;down&nbsp;properly.</li>
<li>Your system is almost-locked-up (i.e. the&nbsp;above&nbsp;point)</li>
<li>Your <span class="caps"><span class="caps">UPS</span></span> has about 10 seconds worth of&nbsp;power&nbsp;left</li>
<li>Something is on fire (<span class="geshifilter"><code class="geshifilter-text">lp0</code></span>&nbsp;possibly?)</li>
<li>&#8230;Insert other esoteric failure&nbsp;modes&nbsp;here&#8230;</li>
</ul>

<p>In any of those situations, grab a console keyboard, and type Alt+SysRq+s (sync), Alt+SysRq+u (unmount), wait for it to have synced, and finally Alt+SysRq+b (reboot <span class="caps"><span class="caps">NOW</span></span>!).
If you don&#8217;t have a handy keyboard attached to said machine, or are on another continent,&nbsp;you&nbsp;can</p>

<div class="geshifilter"><div class="text geshifilter-text" style="font-family:monospace;"># echo u &gt; /proc/sysrq-trigger</div></div>

<p>In my books, the useful SysRq&nbsp;commands&nbsp;are:</p>

<dl>
<dt>b</dt>
<dd>Reboot</dd>

<dt>f</dt>
<dd>Call the oom_killer</dd>

<dt>h</dt>
<dd>Display SysRq help</dd>

<dt>l</dt>
<dd>Print a kernel stacktrace</dd>

<dt>o</dt>
<dd>Power Off</dd>

<dt>r</dt>
<dd>Set your keyboard to <span class="caps"><span class="caps">RAW</span></span> mode (required after some X breakages)</dd>

<dt>s</dt>
<dd>Sync all filesystems</dd>

<dt>u</dt>
<dd>Remount all filesystems read-only</dd>

<dt>0-9</dt>
<dd>Change console logging level</dd>
</dl>

<p>In fact, read the <a href="http://kernel.org/doc/Documentation/sysrq.txt">rest of the SysRq documentation</a>, print it out, and tape it above your bed.
Next time you reach for the reset switch on a Linux box, stop your self, type the S,U,B sequence, and watch your system come up as if nothing untoward&nbsp;has&nbsp;happened.</p>

<p><strong>Update</strong>: I previously recommended U,S,B but after a bit of digging, I think S,U,B may&nbsp;be&nbsp;correct.</p>
    ]]></summary>
    <content type="html"><![CDATA[<p>I&#8217;m constantly surprised when I come across long-time Linux users who don&#8217;t know about SysRq.
The Linux Magic System Request Key Hacks are a magic set of commands that you can get the Linux kernel to follow no matter what&#8217;s going on (unless it has panicked or totally&nbsp;deadlocked).</p>

<p>Why is this useful? Well, there are many situations where you can&#8217;t shut a system down properly, but you need to reboot.&nbsp;Examples:</p>

<ul>
<li>You&#8217;ve had a kernel <span class="caps">OOPS</span>, which is not quite a panic but there could be memory corruption in the kernel, things are getting pretty weird, and quite honestly you don&#8217;t want to be running in that condition for any longer than&nbsp;necessary.</li>
<li>You have reason to believe it won&#8217;t be able to shut down&nbsp;properly.</li>
<li>Your system is almost-locked-up (i.e. the above&nbsp;point)</li>
<li>Your <span class="caps">UPS</span> has about 10 seconds worth of power&nbsp;left</li>
<li>Something is on fire (<code>lp0</code>&nbsp;possibly?)</li>
<li>&#8230;Insert other esoteric failure modes&nbsp;here&#8230;</li>
</ul>

<p>In any of those situations, grab a console keyboard, and type Alt+SysRq+s (sync), Alt+SysRq+u (unmount), wait for it to have synced, and finally Alt+SysRq+b (reboot <span class="caps">NOW</span>!).
If you don&#8217;t have a handy keyboard attached to said machine, or are on another continent, you&nbsp;can</p>

<div class="geshifilter"><div class="text geshifilter-text" style="font-family:monospace;"># echo u &gt; /proc/sysrq-trigger</div></div>

<p>In my books, the useful SysRq commands&nbsp;are:</p>

<dl>
<dt>b</dt>
<dd>Reboot</dd>

<dt>f</dt>
<dd>Call the oom_killer</dd>

<dt>h</dt>
<dd>Display SysRq help</dd>

<dt>l</dt>
<dd>Print a kernel stacktrace</dd>

<dt>o</dt>
<dd>Power Off</dd>

<dt>r</dt>
<dd>Set your keyboard to <span class="caps">RAW</span> mode (required after some X breakages)</dd>

<dt>s</dt>
<dd>Sync all filesystems</dd>

<dt>u</dt>
<dd>Remount all filesystems read-only</dd>

<dt>0-9</dt>
<dd>Change console logging level</dd>
</dl>

<p>In fact, read the <a href="http://kernel.org/doc/Documentation/sysrq.txt">rest of the SysRq documentation</a>, print it out, and tape it above your bed.
Next time you reach for the reset switch on a Linux box, stop your self, type the S,U,B sequence, and watch your system come up as if nothing untoward has&nbsp;happened.</p>

<p><strong>Update</strong>: I previously recommended U,S,B but after a bit of digging, I think S,U,B may be&nbsp;correct.</p>
    ]]></content>
  </entry>
  <entry>
    <title>Drupal Hacking</title>
    <link rel="alternate" type="text/html" href="http://tumbleweed.org.za/2008/09/19/drupal-hacking" />
    <id>http://tumbleweed.org.za/2008/09/19/drupal-hacking</id>
    <published>2008-09-19T12:15:16+00:00</published>
    <updated>2008-09-19T12:15:16+00:00</updated>
    <author>
      <name>tumbleweed</name>
    </author>
    <category term="drupal" />
    <category term="technical" />
    <category term="website" />
    <summary type="html"><![CDATA[<p>I apologise for my last post on this topic, it probably wasn't very interesting :-)</p>

<p>I've done the Drupal 6 upgrade, and it was relatively painless. Most modules ported smoothly, a few required me to learn how to port modules to Drupal 6, and one I just gave up on.</p>

<p>On the whole, the porting is simple, Druplal.org has <a href="http://drupal.org/update/modules">a pretty good howto</a> on the topic. A few APIs have changed, and that's about it. A great tool to help with this is the <a href="http://drupal.org/project/coder">coder</a> module, which knows about the API changes, as well as Drupal's <a href="http://drupal.org/coding-standards">coding standards</a>.</p>

<p>I've added the <a href="http://drupal.org/project/geshifilter">GeSHi</a> module for code syntax highlighting (apologies for the planet-spam caused by this), and I've moved from <a href="http://drupal.org/project/marksmarty">marksmarty</a> to <a href="http://drupal.org/project/markdown">markdown</a> + <a href="http://drupal.org/project/typogrify">typogrify</a> (which I had to <a href="http://drupal.org/node/308400#comment-1018480">port to Drupal 6</a>). I'm not too happy with the geshi colour-scheme and indenting, but it does a good enough job. I should write a "command prompt" mode for it, but that can wait for now...</p>

<p><a href="http://drupal.org/project/akismet">Akismet</a> is currently totally broken for Drupal 6, even if it's labelled as being in beta. I got about half way through porting it before giving up and switching to <a href="http://drupal.org/project/mollom">mollom</a>, which looks like a pretty good replacement (and it takes care of the sign-up form too).</p>

<p>Finally, the subject of input-filters. Drupal lets you define a "default filter", but that filter has to be available for everyone, even comments. So your default filter has to protect against XSS. I'd much prefer it if commenters used a simple, locked-down input-format, and I used a nice markdown format.</p>

<p>I'm <a href="http://groups.drupal.org/node/8911">not the only one to notice this</a>, and it seems like it'll be fixed in Drupal 7. Until then, I'm using <a href="http://drupal.org/project/remember_filter">remember-filter</a> which remembers that I use markdown, and all the commenters use the default, locked-down filter. (Again, <a href="http://drupal.org/node/235380#comment-1017934">ported</a>.)</p>
    ]]></summary>
    <content type="html"><![CDATA[<p>I apologise for my last post on this topic, it probably wasn't very interesting :-)</p>

<p>I've done the Drupal 6 upgrade, and it was relatively painless. Most modules ported smoothly, a few required me to learn how to port modules to Drupal 6, and one I just gave up on.</p>

<p>On the whole, the porting is simple, Druplal.org has <a href="http://drupal.org/update/modules">a pretty good howto</a> on the topic. A few APIs have changed, and that's about it. A great tool to help with this is the <a href="http://drupal.org/project/coder">coder</a> module, which knows about the API changes, as well as Drupal's <a href="http://drupal.org/coding-standards">coding standards</a>.</p>

<p>I've added the <a href="http://drupal.org/project/geshifilter">GeSHi</a> module for code syntax highlighting (apologies for the planet-spam caused by this), and I've moved from <a href="http://drupal.org/project/marksmarty">marksmarty</a> to <a href="http://drupal.org/project/markdown">markdown</a> + <a href="http://drupal.org/project/typogrify">typogrify</a> (which I had to <a href="http://drupal.org/node/308400#comment-1018480">port to Drupal 6</a>). I'm not too happy with the geshi colour-scheme and indenting, but it does a good enough job. I should write a "command prompt" mode for it, but that can wait for now...</p>

<p><a href="http://drupal.org/project/akismet">Akismet</a> is currently totally broken for Drupal 6, even if it's labelled as being in beta. I got about half way through porting it before giving up and switching to <a href="http://drupal.org/project/mollom">mollom</a>, which looks like a pretty good replacement (and it takes care of the sign-up form too).</p>

<p>Finally, the subject of input-filters. Drupal lets you define a "default filter", but that filter has to be available for everyone, even comments. So your default filter has to protect against XSS. I'd much prefer it if commenters used a simple, locked-down input-format, and I used a nice markdown format.</p>

<p>I'm <a href="http://groups.drupal.org/node/8911">not the only one to notice this</a>, and it seems like it'll be fixed in Drupal 7. Until then, I'm using <a href="http://drupal.org/project/remember_filter">remember-filter</a> which remembers that I use markdown, and all the commenters use the default, locked-down filter. (Again, <a href="http://drupal.org/node/235380#comment-1017934">ported</a>.)</p>
    ]]></content>
  </entry>
  <entry>
    <title>Fixing a Digital Camera</title>
    <link rel="alternate" type="text/html" href="http://tumbleweed.org.za/2008/07/13/fixing-digital-camera" />
    <id>http://tumbleweed.org.za/2008/07/13/fixing-digital-camera</id>
    <published>2008-07-13T00:57:11+00:00</published>
    <updated>2008-07-13T19:56:25+00:00</updated>
    <author>
      <name>tumbleweed</name>
    </author>
    <category term="canon" />
    <category term="digital camera" />
    <category term="error 18" />
    <category term="fix" />
    <category term="howto" />
    <category term="ixus" />
    <category term="technical" />
    <summary type="html"><![CDATA[<p>My brother was talking about buying my Canon Digital <span class="caps"><span class="caps">IXUS</span></span> 750 Camera off me. (or PowerShot <span class="caps"><span class="caps">SD550</span></span> for Americans)
He had an identical camera and waterproof housing for it (this costs significantly more than the camera). But said waterproof housing had not been properly&nbsp;closed&nbsp;once&#8230;</p>

<p>My camera wasn&#8217;t having any of that, and the next time I turned it on, it half-opened the lens, groaned, and said &#8220;E18&#8221;. Bugger. Googling E18 turned up a few sites showing other people with the same problem, <em>lots</em> of other people: <a href="http://www.e18error.com/">e18error.com</a>, <a href="http://en.wikipedia.org/wiki/E18_Error">E18 Error on Wikipedia</a>. It appears to be a generic error for lens problems in Canon cameras, and occurs so often that class action suits have been filed&nbsp;against&nbsp;Canon.</p>

<p>I read the tales of woe, and tried the suggested remedies of shaking, banging, prodding, and otherwise mauling my poor camera. Nothing helped. I put it in a <a href="http://en.wikipedia.org/wiki/Pelican_case">pelican case</a> and forgot about it  for a&nbsp;few&nbsp;weeks.</p>

<p>Reading on, I discovered a few tales of brave owners disassembling and repairing their cameras, mostly successfully. As a geek, I knew I was going to <em>have to</em> give this a shot. I&#8217;ve taken things apart since I learned how to use a screwdriver, so I can normally put them back together again (these days), <em>and</em> they normally&nbsp;still&nbsp;work.</p>

<p>Eventually, I got around to this, last week. Nobody has posted disassembly instructions for any camera near my model, so I had to work it out for myself. Now, let&#8217;s&nbsp;remedy&nbsp;that:</p>

<h1>My&nbsp;Symptoms</h1>

<p>My camera&#8217;s lens was open, and wouldn&#8217;t move at all. Turning it on gave an&nbsp;E18&nbsp;error.</p>

<h1>Preparation</h1>

<p><img src="/files/equipment.jpg" alt="My equipment" /></p>

<p>I&#8217;d recommend&nbsp;the&nbsp;following:</p>

<ul>
<li>An afternoon <span class="amp">&amp;</span> evening&nbsp;to&nbsp;yourself</li>
<li>A large, empty desk (preferably with a lip, to catch&nbsp;dropped&nbsp;screws)</li>
<li>A lino floor (carpets can lose&nbsp;dropped&nbsp;screws)</li>
<li>A good&nbsp;desk&nbsp;light</li>
<li>Lens tissue (or better yet, the&nbsp;wet-wipe&nbsp;version)</li>
<li>Meths and ear-buds (or other solvent&nbsp;of&nbsp;choice)</li>
<li>Superglue (in case you break something or something&nbsp;is&nbsp;broken)</li>
<li>Tweezers, pliers, leatherman, etc. (you are dealing with lots of&nbsp;little&nbsp;things)</li>
<li>Screwdrivers: small philips-head drivers for screws, and a few tiny flat ones&nbsp;for&nbsp;prying.</li>
<li>A torch (to help you find&nbsp;dropped&nbsp;screws)</li>
<li>A third-hand (or at least its&nbsp;magnifying&nbsp;glass)</li>
<li>A blower/brush (to get rid&nbsp;of&nbsp;dirt)</li>
<li>A working camera (to document the procedure, so you can put it&nbsp;back&nbsp;together)</li>
<li>Patience - dropped screws can be hard&nbsp;to&nbsp;find</li>
</ul>

<p>Warnings: You need to have a willingness to part with your patient&#8217;s life. You also need to be aware that camera flash assemblies contain high-voltage capacitors, that usually hold a small residual charge. Stay well clear of them and their circuitry. If possible, discharge it as soon as you see it, with a&nbsp;heavy-duty&nbsp;resistor.</p>

<p>Tips: Lay out the removed parts in the order you disassembled them, together with their screws. That way you won&#8217;t have the &#8220;left-over screw&#8221; problem or put things together in the&nbsp;wrong&nbsp;order.</p>

<h1>Disassembly</h1>

<p>Remove the battery and <span class="caps"><span class="caps">SD</span></span>&nbsp;card.</p>

<p><img src="/files/case.jpg" alt="Unscrewing the case" /></p>

<p>To remove the case, you need to undo all the exterior screws: 3 on the base, 2 on the left, and 2 on the right (one is under flap C). The side plate A is loose, and B is a plastic sheet that can be pulled out, revealing an additional screw. Flap C is attached to the body, not B. When reassembling, take care to insert lip D under the&nbsp;back&nbsp;panel.</p>

<p>There are no clips on the bottom or sides, but there are 3 along the top, between the front and back halves. One to the right of the shutter, two to the left. Pry up on the&nbsp;front&nbsp;half.</p>

<p><img src="/files/opened.jpg" alt="Opened" /></p>

<p>There should be a black O-ring on the outer part of the lens. Lift it off&nbsp;and&nbsp;store.</p>

<p><img src="/files/parts-connectors.jpg" alt="Parts and Connectors" /></p>

<p>The three main modules are now visible. Motherboard and battery (A), Flash unit (B), and Optics (C). While we won&#8217;t undo these connectors quite yet, as the <span class="caps"><span class="caps">LCD</span></span> is currently attached to both sides, but this is a good opportunity to explain the connectors that you&#8217;ll&nbsp;be&nbsp;encountering.</p>

<p>The ribbon cable E plugs into the white connector with a black lid. The black lid needs to be folded back for the ribbon to be removed. It simply pulls out along it&#8217;s axis. To re-insert: open, push in ribbon as far as it&#8217;ll go, and close. These connectors are quite delicate,&nbsp;be&nbsp;careful.</p>

<p>There is another type of ribbon connector which simply relies on friction. The <span class="caps"><span class="caps">LCD</span></span> back-light cable is an example. You just pull it out with tweezers, and push it back with tweezers (without bending it,&nbsp;if&nbsp;possible).</p>

<p>The flash power lead D must be pulled up, away from the camera. Insert a tiny screwdriver underneath the wires at the point indicated, and&nbsp;pry&nbsp;up.</p>

<p><img src="/files/lcd.jpg" alt="LCD and back" /></p>

<p>The buttons are a loose piece of rubber.&nbsp;Lift&nbsp;off.</p>

<p>The <span class="caps"><span class="caps">LCD</span></span> needs to be removed first. Pull out the backlight power ribbon (A). Unscrew the screw above the <span class="caps"><span class="caps">LCD</span></span>, releasing a bar. The left side has a small clip that needs to be released, and then the <span class="caps"><span class="caps">LCD</span></span>-backlight assembly should lift upwards. The right hand side has a lip under the keypad module, so lift the left side first. You won&#8217;t be able to disconnect the <span class="caps"><span class="caps">LCD</span></span> ribbon until you remove the&nbsp;keypad&nbsp;plate.</p>

<p>Unscrew the 2 screws at the top of the keypad plate. There are a few clips holding the bottom in place (arrowed). But you should be able to pull the plate away, revealing the ribbon connectors for both units. Unplug&nbsp;them&nbsp;both.</p>

<p><img src="/files/flash-unit.jpg" alt="Flash Unit and Optical Assembly" /></p>

<p>The Flash unit can now be removed. Unplug the cables shown earlier, as well as the screw on the bottom right-corner. The ribbon plugs into the flash unit,&nbsp;unplug&nbsp;(C).</p>

<p>The left two screws on the back (red) will release the&nbsp;flash&nbsp;unit.</p>

<p>Before unscrewing the optical assembly, open the <span class="caps"><span class="caps">CCD</span></span> ribbon connector (A). When re-attaching the module, the cable should again be inserted first, and&nbsp;locked&nbsp;last.</p>

<p>The three (green) screws on the metal frame will release the optical module. Beware a tiny spring hiding under B. Lift it out, and&nbsp;store&nbsp;it.</p>

<p><img src="/files/optical-assembly.jpg" alt="Optical Assembly" /></p>

<p>Before we can take the Optical assembly apart, the focussing <span class="caps"><span class="caps">LED</span></span> has to be removed. Unstick ribbon A, and pry up the <span class="caps"><span class="caps">LED</span></span> (B). Continue lifting the ribbon, unsticking the status <span class="caps"><span class="caps">LED</span></span> section&nbsp;(C),&nbsp;too.</p>

<p>While we are here, the focussing servo&#8217;s cogs are under D, if you are cog-cleaning. Don&#8217;t open if you don&#8217;t&nbsp;need&nbsp;to.</p>

<p>Unplug (pull) the shutter-ribbon from E, and unstick E&#8217;s ribbon from&nbsp;the&nbsp;lens-body.</p>

<p>The lens and viewfinder assembly can now be removed from the base-plate with the <span class="caps"><span class="caps">CCD</span></span> and motors. Unscrew the 4 screws and one on the base. The long screw comes from near F. Lift up the lens carefully. A small black cog will be loose near F. Remove&nbsp;and&nbsp;store.</p>

<p>The green screw gives access to the zoom servo&#8217;s cogs. Don&#8217;t open unless you&nbsp;need&nbsp;to.</p>

<p><img src="/files/ccd.jpg" alt="CCD" /></p>

<p>On the <span class="caps"><span class="caps">CCD</span></span> base, the sharp bit (A) activates the lens-cap mechanism in the lens, when it&#8217;s closed. The lens element (B) is for focussing, and in my case it&#8217;s sitting at an odd angle, because the short pin (circled) had broken, and had to be glued back in place. This pin passes through an <span class="caps"><span class="caps">IR</span></span> light-switch when the lens is at a certain hight, allowing the camera to calibrate&nbsp;its&nbsp;focus.</p>

<p>Check that the focussing element moves up and down smoothly when you rotate the thread&nbsp;below&nbsp;A.</p>

<p>While you are here, blow any dust off the lens and <span class="caps"><span class="caps">CCD</span></span>&nbsp;below&nbsp;it.</p>

<p><img src="/files/lens.jpg" alt="Lens" /></p>

<p>To disassemble the lens: Un-thread the ribbon. Roll the big cog on the side until it&#8217;s fully closed, and clicks, revealing the pins of the inner rings, and push the outermost interior ring of the lens backwards from the front. It should&nbsp;pop&nbsp;out.</p>

<p><img src="/files/lens-rings.jpg" alt="Lens Rings" /></p>

<p>The rings either simply pop out backwards, or have a track leading to the surface. Clean all the tracks&nbsp;and&nbsp;pins.</p>

<p>If you are having shutter-trouble, you can open the innermost module, but beware it&#8217;s delicate. If the lens-cap is jamming, operate it a bit with a screwdriver (wiggle), blow air at it, etc until it&nbsp;works&nbsp;cleanly.</p>

<h1>Reassembly</h1>

<p>Finally, if you found your&nbsp;problem,&nbsp;reassemble.</p>

<p>Remember to rethread the lens ribbon before you attach the outermost ring. The lens should operate smoothly when zoomed with the big cog. It&#8217;s easiest if you attach it to the <span class="caps"><span class="caps">CCD</span></span> plate in the&nbsp;opened&nbsp;state.</p>

<p>The camera behaves well, and can be tested disassembled. If you are having E18 trouble, you can just connect the lens to the motherboard, insert the battery, and turn it on. If it&#8217;s working, the lens should open, and close when turned off (and the power button <span class="caps"><span class="caps">LED</span></span> should go out promptly, if it doesn&#8217;t you haven&#8217;t found the&nbsp;trouble&nbsp;yet).</p>

<p>Enjoy your newly fixed camera. I&nbsp;am,&nbsp;mine.</p>
    ]]></summary>
    <content type="html"><![CDATA[<p>My brother was talking about buying my Canon Digital <span class="caps">IXUS</span> 750 Camera off me. (or PowerShot <span class="caps">SD550</span> for Americans)
He had an identical camera and waterproof housing for it (this costs significantly more than the camera). But said waterproof housing had not been properly closed&nbsp;once&#8230;</p>

<p>My camera wasn&#8217;t having any of that, and the next time I turned it on, it half-opened the lens, groaned, and said &#8220;E18&#8221;. Bugger. Googling E18 turned up a few sites showing other people with the same problem, <em>lots</em> of other people: <a href="http://www.e18error.com/">e18error.com</a>, <a href="http://en.wikipedia.org/wiki/E18_Error">E18 Error on Wikipedia</a>. It appears to be a generic error for lens problems in Canon cameras, and occurs so often that class action suits have been filed against&nbsp;Canon.</p>

<p>I read the tales of woe, and tried the suggested remedies of shaking, banging, prodding, and otherwise mauling my poor camera. Nothing helped. I put it in a <a href="http://en.wikipedia.org/wiki/Pelican_case">pelican case</a> and forgot about it  for a few&nbsp;weeks.</p>

<p>Reading on, I discovered a few tales of brave owners disassembling and repairing their cameras, mostly successfully. As a geek, I knew I was going to <em>have to</em> give this a shot. I&#8217;ve taken things apart since I learned how to use a screwdriver, so I can normally put them back together again (these days), <em>and</em> they normally still&nbsp;work.</p>

<p>Eventually, I got around to this, last week. Nobody has posted disassembly instructions for any camera near my model, so I had to work it out for myself. Now, let&#8217;s remedy&nbsp;that:</p>

<h1>My&nbsp;Symptoms</h1>

<p>My camera&#8217;s lens was open, and wouldn&#8217;t move at all. Turning it on gave an E18&nbsp;error.</p>

<h1>Preparation</h1>

<p><img src="/files/equipment.jpg" alt="My equipment" /></p>

<p>I&#8217;d recommend the&nbsp;following:</p>

<ul>
<li>An afternoon <span class="amp">&amp;</span> evening to&nbsp;yourself</li>
<li>A large, empty desk (preferably with a lip, to catch dropped&nbsp;screws)</li>
<li>A lino floor (carpets can lose dropped&nbsp;screws)</li>
<li>A good desk&nbsp;light</li>
<li>Lens tissue (or better yet, the wet-wipe&nbsp;version)</li>
<li>Meths and ear-buds (or other solvent of&nbsp;choice)</li>
<li>Superglue (in case you break something or something is&nbsp;broken)</li>
<li>Tweezers, pliers, leatherman, etc. (you are dealing with lots of little&nbsp;things)</li>
<li>Screwdrivers: small philips-head drivers for screws, and a few tiny flat ones for&nbsp;prying.</li>
<li>A torch (to help you find dropped&nbsp;screws)</li>
<li>A third-hand (or at least its magnifying&nbsp;glass)</li>
<li>A blower/brush (to get rid of&nbsp;dirt)</li>
<li>A working camera (to document the procedure, so you can put it back&nbsp;together)</li>
<li>Patience - dropped screws can be hard to&nbsp;find</li>
</ul>

<p>Warnings: You need to have a willingness to part with your patient&#8217;s life. You also need to be aware that camera flash assemblies contain high-voltage capacitors, that usually hold a small residual charge. Stay well clear of them and their circuitry. If possible, discharge it as soon as you see it, with a heavy-duty&nbsp;resistor.</p>

<p>Tips: Lay out the removed parts in the order you disassembled them, together with their screws. That way you won&#8217;t have the &#8220;left-over screw&#8221; problem or put things together in the wrong&nbsp;order.</p>

<h1>Disassembly</h1>

<p>Remove the battery and <span class="caps">SD</span>&nbsp;card.</p>

<p><img src="/files/case.jpg" alt="Unscrewing the case" /></p>

<p>To remove the case, you need to undo all the exterior screws: 3 on the base, 2 on the left, and 2 on the right (one is under flap C). The side plate A is loose, and B is a plastic sheet that can be pulled out, revealing an additional screw. Flap C is attached to the body, not B. When reassembling, take care to insert lip D under the back&nbsp;panel.</p>

<p>There are no clips on the bottom or sides, but there are 3 along the top, between the front and back halves. One to the right of the shutter, two to the left. Pry up on the front&nbsp;half.</p>

<p><img src="/files/opened.jpg" alt="Opened" /></p>

<p>There should be a black O-ring on the outer part of the lens. Lift it off and&nbsp;store.</p>

<p><img src="/files/parts-connectors.jpg" alt="Parts and Connectors" /></p>

<p>The three main modules are now visible. Motherboard and battery (A), Flash unit (B), and Optics (C). While we won&#8217;t undo these connectors quite yet, as the <span class="caps">LCD</span> is currently attached to both sides, but this is a good opportunity to explain the connectors that you&#8217;ll be&nbsp;encountering.</p>

<p>The ribbon cable E plugs into the white connector with a black lid. The black lid needs to be folded back for the ribbon to be removed. It simply pulls out along it&#8217;s axis. To re-insert: open, push in ribbon as far as it&#8217;ll go, and close. These connectors are quite delicate, be&nbsp;careful.</p>

<p>There is another type of ribbon connector which simply relies on friction. The <span class="caps">LCD</span> back-light cable is an example. You just pull it out with tweezers, and push it back with tweezers (without bending it, if&nbsp;possible).</p>

<p>The flash power lead D must be pulled up, away from the camera. Insert a tiny screwdriver underneath the wires at the point indicated, and pry&nbsp;up.</p>

<p><img src="/files/lcd.jpg" alt="LCD and back" /></p>

<p>The buttons are a loose piece of rubber. Lift&nbsp;off.</p>

<p>The <span class="caps">LCD</span> needs to be removed first. Pull out the backlight power ribbon (A). Unscrew the screw above the <span class="caps">LCD</span>, releasing a bar. The left side has a small clip that needs to be released, and then the <span class="caps">LCD</span>-backlight assembly should lift upwards. The right hand side has a lip under the keypad module, so lift the left side first. You won&#8217;t be able to disconnect the <span class="caps">LCD</span> ribbon until you remove the keypad&nbsp;plate.</p>

<p>Unscrew the 2 screws at the top of the keypad plate. There are a few clips holding the bottom in place (arrowed). But you should be able to pull the plate away, revealing the ribbon connectors for both units. Unplug them&nbsp;both.</p>

<p><img src="/files/flash-unit.jpg" alt="Flash Unit and Optical Assembly" /></p>

<p>The Flash unit can now be removed. Unplug the cables shown earlier, as well as the screw on the bottom right-corner. The ribbon plugs into the flash unit, unplug&nbsp;(C).</p>

<p>The left two screws on the back (red) will release the flash&nbsp;unit.</p>

<p>Before unscrewing the optical assembly, open the <span class="caps">CCD</span> ribbon connector (A). When re-attaching the module, the cable should again be inserted first, and locked&nbsp;last.</p>

<p>The three (green) screws on the metal frame will release the optical module. Beware a tiny spring hiding under B. Lift it out, and store&nbsp;it.</p>

<p><img src="/files/optical-assembly.jpg" alt="Optical Assembly" /></p>

<p>Before we can take the Optical assembly apart, the focussing <span class="caps">LED</span> has to be removed. Unstick ribbon A, and pry up the <span class="caps">LED</span> (B). Continue lifting the ribbon, unsticking the status <span class="caps">LED</span> section (C),&nbsp;too.</p>

<p>While we are here, the focussing servo&#8217;s cogs are under D, if you are cog-cleaning. Don&#8217;t open if you don&#8217;t need&nbsp;to.</p>

<p>Unplug (pull) the shutter-ribbon from E, and unstick E&#8217;s ribbon from the&nbsp;lens-body.</p>

<p>The lens and viewfinder assembly can now be removed from the base-plate with the <span class="caps">CCD</span> and motors. Unscrew the 4 screws and one on the base. The long screw comes from near F. Lift up the lens carefully. A small black cog will be loose near F. Remove and&nbsp;store.</p>

<p>The green screw gives access to the zoom servo&#8217;s cogs. Don&#8217;t open unless you need&nbsp;to.</p>

<p><img src="/files/ccd.jpg" alt="CCD" /></p>

<p>On the <span class="caps">CCD</span> base, the sharp bit (A) activates the lens-cap mechanism in the lens, when it&#8217;s closed. The lens element (B) is for focussing, and in my case it&#8217;s sitting at an odd angle, because the short pin (circled) had broken, and had to be glued back in place. This pin passes through an <span class="caps">IR</span> light-switch when the lens is at a certain hight, allowing the camera to calibrate its&nbsp;focus.</p>

<p>Check that the focussing element moves up and down smoothly when you rotate the thread below&nbsp;A.</p>

<p>While you are here, blow any dust off the lens and <span class="caps">CCD</span> below&nbsp;it.</p>

<p><img src="/files/lens.jpg" alt="Lens" /></p>

<p>To disassemble the lens: Un-thread the ribbon. Roll the big cog on the side until it&#8217;s fully closed, and clicks, revealing the pins of the inner rings, and push the outermost interior ring of the lens backwards from the front. It should pop&nbsp;out.</p>

<p><img src="/files/lens-rings.jpg" alt="Lens Rings" /></p>

<p>The rings either simply pop out backwards, or have a track leading to the surface. Clean all the tracks and&nbsp;pins.</p>

<p>If you are having shutter-trouble, you can open the innermost module, but beware it&#8217;s delicate. If the lens-cap is jamming, operate it a bit with a screwdriver (wiggle), blow air at it, etc until it works&nbsp;cleanly.</p>

<h1>Reassembly</h1>

<p>Finally, if you found your problem,&nbsp;reassemble.</p>

<p>Remember to rethread the lens ribbon before you attach the outermost ring. The lens should operate smoothly when zoomed with the big cog. It&#8217;s easiest if you attach it to the <span class="caps">CCD</span> plate in the opened&nbsp;state.</p>

<p>The camera behaves well, and can be tested disassembled. If you are having E18 trouble, you can just connect the lens to the motherboard, insert the battery, and turn it on. If it&#8217;s working, the lens should open, and close when turned off (and the power button <span class="caps">LED</span> should go out promptly, if it doesn&#8217;t you haven&#8217;t found the trouble&nbsp;yet).</p>

<p>Enjoy your newly fixed camera. I am,&nbsp;mine.</p>
    ]]></content>
  </entry>
  <entry>
    <title>Gammu with Samsung</title>
    <link rel="alternate" type="text/html" href="http://tumbleweed.org.za/2008/04/29/gammu-samsung" />
    <id>http://tumbleweed.org.za/2008/04/29/gammu-samsung</id>
    <published>2008-04-29T20:33:29+00:00</published>
    <updated>2008-10-17T12:04:58+00:00</updated>
    <author>
      <name>tumbleweed</name>
    </author>
    <category term="cell-phone" />
    <category term="gammu" />
    <category term="python" />
    <category term="samsung" />
    <category term="technical" />
    <summary type="html"><![CDATA[<p>A housemate of mine got a new Samsung phone on the weekend. Being a resident geek, I offered to transfer her contacts across rather than get her sister to manually retype&nbsp;500-odd&nbsp;contacts.</p>

<p>Naturally, I thought this would be a simple problem, right? I mean, everyone updates their phones every 2 years, this must be a pretty common use case. All my Sony Ericsson phones have had a &#8220;send all contacts by Bluetooth&#8221; option since the inception of Bluetooth. Naturally, it didn&#8217;t have such a feature, it only supports sending one contact at a time. (Although, to Samsung&#8217;s credit, the <em>new</em> phone will be able to do for the&nbsp;next&nbsp;upgrade)</p>

<p>Next option: I&#8217;ll sync old phone to laptop to&nbsp;new&nbsp;phone.</p>

<p>The Samsung website has a helpful Windows utility that you can download to do this, however you need the cable to link the phone to the computer. The phones needed different cables, and I had neither. My laptop with a Windows partition has had broken Bluetooth ever since <a href="/2006/12/29/acer-laptop-woes-part-7">its motherboard got replaced</a>. So that wasn&#8217;t an option. The phones don&#8217;t have <span class="caps"><span class="caps">IRDA</span></span>, so there was no way to connect them with the&nbsp;Windows&nbsp;laptop.</p>

<p>Time to do&nbsp;it&nbsp;properly.</p>

<p>I tried <a href="http://wammu.eu/">wammu</a>, a python-based <a href="http://www.gammu.org/">gammu</a> <span class="caps"><span class="caps">GUI</span></span>. It supported the phones via the &#8220;blueat&#8221; driver, and could browse their <span class="caps"><span class="caps">SIM</span></span> cards fine, but not their internal Phonebooks. It couldn&#8217;t back them up either. A bit of poking around with gammu on the command line showed that the internal phone books are not 0-indexed (normal computer counting, 0 to n-1) or 1-indexed (normal human counting, 1 to n), but 2-indexed. Dijkstra would turn in&nbsp;his&nbsp;grave!</p>

<p>At this point, I could see that I was going to have to write my own, backup utility. The output of gammu was awkable, but seeing as there are good gammu-python bindings, I decided to do it in&nbsp;pure&nbsp;Python.</p>

<p>Reading the address book went something&nbsp;like&nbsp;this:</p>

<div class="geshifilter"><div class="python geshifilter-python" style="font-family:monospace;"><span class="kw1">import</span> gammu, <span class="kw3">pickle</span><br />
sm = gammu.<span class="me1">StateMachine</span><span class="br0">&#40;</span><span class="br0">&#41;</span><br />
sm.<span class="me1">ReadConfig</span><span class="br0">&#40;</span><span class="nu0">3</span>, <span class="nu0">0</span><span class="br0">&#41;</span><br />
sm.<span class="me1">Init</span><span class="br0">&#40;</span><span class="br0">&#41;</span><br />
old = <span class="br0">&#91;</span><span class="br0">&#93;</span><br />
<span class="kw1">for</span> i <span class="kw1">in</span> <span class="kw2">range</span><span class="br0">&#40;</span><span class="nu0">2</span>, <span class="nu0">587</span><span class="br0">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; old.<span class="me1">append</span><span class="br0">&#40;</span>sm.<span class="me1">GetMemory</span><span class="br0">&#40;</span><span class="st0">&quot;<span class="caps"><span class="caps">ME</span></span>&quot;</span>, i<span class="br0">&#41;</span><span class="br0">&#41;</span><br />
<br />
<span class="kw3">pickle</span>.<span class="me1">dump</span><span class="br0">&#40;</span>old, <span class="kw2">file</span><span class="br0">&#40;</span><span class="st0">&quot;phonebook.dump&quot;</span>, <span class="st0">&quot;w&quot;</span><span class="br0">&#41;</span><span class="br0">&#41;</span></div></div>

<p>The 3 signifies gammu configuration number 3, read into position 0. 587 is the number of address book entries. &#8220;<span class="caps"><span class="caps">ME</span></span>&#8221; means internal memory. I then pickled &#8220;old&#8221; in preparation for the next stage. Here is an example of an item&nbsp;in&nbsp;old:</p>

<div class="geshifilter"><div class="python geshifilter-python" style="font-family:monospace;"><span class="br0">&#123;</span><span class="st0">&#8216;Entries&#8217;</span>: <span class="br0">&#91;</span><span class="br0">&#123;</span><span class="st0">&#8216;AddError&#8217;</span>: <span class="nu0">7517792</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">&#8216;Type&#8217;</span>: <span class="st0">&#8216;Text_FirstName&#8217;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">&#8216;Value&#8217;</span>: u<span class="st0">&#8216;Foo&#8217;</span><span class="br0">&#125;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="br0">&#123;</span><span class="st0">&#8216;AddError&#8217;</span>: <span class="nu0">796160623</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">&#8216;Type&#8217;</span>: <span class="st0">&#8216;Text_LastName&#8217;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">&#8216;Value&#8217;</span>: u<span class="st0">&#8216;Bar&#8217;</span><span class="br0">&#125;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="br0">&#123;</span><span class="st0">&#8216;AddError&#8217;</span>: <span class="nu0">796160623</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">&#8216;SMSList&#8217;</span>: <span class="br0">&#91;</span><span class="br0">&#93;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">&#8216;Type&#8217;</span>: <span class="st0">&#8216;Number_Other&#8217;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">&#8216;Value&#8217;</span>: u<span class="st0">&#8216;0211234567&#8217;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">&#8216;VoiceTag&#8217;</span>: <span class="nu0">0</span><span class="br0">&#125;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="br0">&#123;</span><span class="st0">&#8216;Type&#8217;</span>: <span class="st0">&#8216;Category&#8217;</span>, <span class="st0">&#8216;Value&#8217;</span>: <span class="nu0">0</span><span class="br0">&#125;</span><span class="br0">&#93;</span>,<br />
&nbsp;<span class="st0">&#8216;Location&#8217;</span>: <span class="nu0">2</span>,<br />
&nbsp;<span class="st0">&#8216;MemoryType&#8217;</span>: <span class="st0">&#8216;<span class="caps"><span class="caps">ME</span></span>&#8217;</span><span class="br0">&#125;</span></div></div>

<p>Pretty icky, but at least all the information is there. At this point, one should be able to feed it into the&nbsp;new&nbsp;phone:</p>

<div class="geshifilter"><div class="python geshifilter-python" style="font-family:monospace;">sm.<span class="me1">Terminate</span><span class="br0">&#40;</span><span class="br0">&#41;</span><br />
sm = gammu.<span class="me1">StateMachine</span><span class="br0">&#40;</span><span class="br0">&#41;</span><br />
sm.<span class="me1">ReadConfig</span><span class="br0">&#40;</span><span class="nu0">4</span>, <span class="nu0">0</span><span class="br0">&#41;</span><br />
sm.<span class="me1">Init</span><span class="br0">&#40;</span><span class="br0">&#41;</span><br />
<span class="kw1">for</span> i <span class="kw1">in</span> old:<br />
&nbsp; &nbsp; &nbsp; &nbsp; sm.<span class="me1">AddMemory</span><span class="br0">&#40;</span>i<span class="br0">&#41;</span></div></div>

<p>However nothing I tried worked, I always got an &#8220;Invalid Location&#8221; error. I think the 2-indexing is trumping&nbsp;gammu&nbsp;again.</p>

<p>Next idea, lets munge the data into vCard format and use wammu / gammu&#8217;s &#8220;import from vCard&#8221; function. (Code coming up soon) Turns out this doesn&#8217;t work either. The phone only received the First name, first phone number, and various other things that I didn&#8217;t send it (i.e. custom ring tones that it made&nbsp;up).&nbsp;Hmph!</p>

<p>Aha, but cellphones can normally Bluetooth vCards to each other. So I pushed it the vCard collection via obexftp. Starts transmitting, but then the phone reboots. I played around a bit, and found that if you send it more than one vCard in a vCard file, it&nbsp;reboots.&nbsp;Lovely.</p>

<p>So my final solution was: Extract address book with python-gammu. Transform into vCards. Send each one individually. At least the phone had a &#8220;trust this device&#8221; option so that it wouldn&#8217;t prompt the user for every vCard I sent, but just automatically import them - the first sensible feature I&#8217;ve found&nbsp;on&nbsp;it.</p>

<p>Here&nbsp;goes:</p>

<div class="geshifilter"><div class="python geshifilter-python" style="font-family:monospace;"><span class="co1">#!/usr/bin/env python</span><br />
<span class="kw1">import</span> <span class="kw3">os</span>, <span class="kw3">pickle</span>, <span class="kw3">time</span><br />
<br />
<span class="kw1">def</span> normalise_num<span class="br0">&#40;</span>n<span class="br0">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">&quot;Neaten up the phone number, internationalise, etc.&quot;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> n.<span class="me1">startswith</span><span class="br0">&#40;</span><span class="st0">&quot;+&quot;</span><span class="br0">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> n<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> n.<span class="me1">startswith</span><span class="br0">&#40;</span><span class="st0">&quot;00&quot;</span><span class="br0">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> <span class="st0">&quot;+&quot;</span> + n<span class="br0">&#91;</span><span class="nu0">2</span>:<span class="br0">&#93;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="kw2">len</span><span class="br0">&#40;</span>n<span class="br0">&#41;</span> == <span class="nu0">10</span> <span class="kw1">and</span> n<span class="br0">&#91;</span><span class="nu0">0</span><span class="br0">&#93;</span> == <span class="st0">&quot;0&quot;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> <span class="st0">&quot;+27&quot;</span> + n<span class="br0">&#91;</span><span class="nu0">1</span>:<span class="br0">&#93;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> n<br />
<br />
d = <span class="kw3">pickle</span>.<span class="me1">load</span><span class="br0">&#40;</span><span class="kw2">file</span><span class="br0">&#40;</span><span class="st0">&quot;phonebook.dump&quot;</span>, <span class="st0">&quot;r&quot;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><br />
<br />
<span class="co1"># Normalise into a sensible format:</span><br />
o = <span class="br0">&#91;</span><span class="br0">&#93;</span><br />
<span class="kw1">for</span> i <span class="kw1">in</span> d:<br />
&nbsp; &nbsp; &nbsp; &nbsp; t = <span class="br0">&#123;</span><span class="br0">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">for</span> j <span class="kw1">in</span> i<span class="br0">&#91;</span><span class="st0">&quot;Entries&quot;</span><span class="br0">&#93;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> j<span class="br0">&#91;</span><span class="st0">&quot;Type&quot;</span><span class="br0">&#93;</span> == <span class="st0">&quot;Text_FirstName&quot;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; t<span class="br0">&#91;</span><span class="st0">&quot;First&quot;</span><span class="br0">&#93;</span> = j<span class="br0">&#91;</span><span class="st0">&quot;Value&quot;</span><span class="br0">&#93;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> j<span class="br0">&#91;</span><span class="st0">&quot;Type&quot;</span><span class="br0">&#93;</span> == <span class="st0">&quot;Text_LastName&quot;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; t<span class="br0">&#91;</span><span class="st0">&quot;Last&quot;</span><span class="br0">&#93;</span> = j<span class="br0">&#91;</span><span class="st0">&quot;Value&quot;</span><span class="br0">&#93;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> j<span class="br0">&#91;</span><span class="st0">&quot;Type&quot;</span><span class="br0">&#93;</span> == <span class="st0">&quot;Number_Other&quot;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; n = normalise_num<span class="br0">&#40;</span>j<span class="br0">&#91;</span><span class="st0">&quot;Value&quot;</span><span class="br0">&#93;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">type</span> = <span class="st0">&quot;Home&quot;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> n<span class="br0">&#91;</span><span class="nu0">3</span><span class="br0">&#93;</span> <span class="kw1">in</span> <span class="br0">&#40;</span><span class="st0">&quot;7&quot;</span>, <span class="st0">&quot;8&quot;</span><span class="br0">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">type</span> = <span class="st0">&quot;Cell&quot;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="kw2">type</span> <span class="kw1">not</span> <span class="kw1">in</span> t:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; t<span class="br0">&#91;</span><span class="kw2">type</span><span class="br0">&#93;</span> = <span class="br0">&#91;</span><span class="br0">&#93;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; t<span class="br0">&#91;</span><span class="kw2">type</span><span class="br0">&#93;</span>.<span class="me1">append</span><span class="br0">&#40;</span>n<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; o.<span class="me1">append</span><span class="br0">&#40;</span>t<span class="br0">&#41;</span><br />
<br />
<span class="co1"># Write <span class="amp">&amp;</span> Send vCards:</span><br />
<span class="kw1">for</span> i <span class="kw1">in</span> o:<br />
&nbsp; &nbsp; &nbsp; &nbsp; f = <span class="kw2">file</span><span class="br0">&#40;</span><span class="st0">&quot;temp.vcf&quot;</span>, <span class="st0">&quot;w&quot;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; f.<span class="me1">write</span><span class="br0">&#40;</span><span class="st0">&quot;<span class="caps"><span class="caps">BEGIN</span></span>:<span class="caps"><span class="caps">VCARD</span></span><span class="es0">\n</span>&quot;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; f.<span class="me1">write</span><span class="br0">&#40;</span><span class="st0">&quot;<span class="caps"><span class="caps">VERSION</span></span>:2.1<span class="es0">\n</span>&quot;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; f.<span class="me1">write</span><span class="br0">&#40;</span><span class="st0">&quot;N:%s;%s;;;<span class="es0">\n</span>&quot;</span> <span class="sy0">%</span> <span class="br0">&#40;</span>i.<span class="me1">get</span><span class="br0">&#40;</span><span class="st0">&quot;Last&quot;</span>, <span class="st0">&quot;&quot;</span><span class="br0">&#41;</span>, i.<span class="me1">get</span><span class="br0">&#40;</span><span class="st0">&quot;First&quot;</span>, <span class="st0">&quot;&quot;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; pref = <span class="st0">&quot;;<span class="caps"><span class="caps">PREF</span></span>&quot;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">for</span> j <span class="kw1">in</span> i<span class="br0">&#91;</span><span class="st0">&quot;Cell&quot;</span><span class="br0">&#93;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; f.<span class="me1">write</span><span class="br0">&#40;</span><span class="st0">&quot;<span class="caps"><span class="caps">TEL</span></span>;<span class="caps"><span class="caps">CELL</span></span>%s:%s<span class="es0">\n</span>&quot;</span> <span class="sy0">%</span> <span class="br0">&#40;</span>pref, j<span class="br0">&#41;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pref=<span class="st0">&quot;&quot;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">for</span> j <span class="kw1">in</span> i<span class="br0">&#91;</span><span class="st0">&quot;Home&quot;</span><span class="br0">&#93;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; f.<span class="me1">write</span><span class="br0">&#40;</span><span class="st0">&quot;<span class="caps"><span class="caps">TEL</span></span>;<span class="caps"><span class="caps">HOME</span></span>%s:%s<span class="es0">\n</span>&quot;</span> <span class="sy0">%</span> <span class="br0">&#40;</span>pref, j<span class="br0">&#41;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pref=<span class="st0">&quot;&quot;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; f.<span class="me1">write</span><span class="br0">&#40;</span><span class="st0">&quot;<span class="caps"><span class="caps">END</span></span>:<span class="caps"><span class="caps">VCARD</span></span><span class="es0">\n</span>&quot;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; f.<span class="me1">close</span><span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">os</span>.<span class="me1">system</span><span class="br0">&#40;</span><span class="st0">&quot;obexftp -b 00:<span class="caps"><span class="caps">DE</span></span>:<span class="caps"><span class="caps">AD</span></span>:00:<span class="caps"><span class="caps">BE</span></span>:<span class="caps"><span class="caps">EF</span></span> -p temp.vcf&quot;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1"># Give the thing a chance to recover:</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">time</span>.<span class="me1">sleep</span><span class="br0">&#40;</span><span class="nu0">0.1</span><span class="br0">&#41;</span></div></div>

<p>Yes, the normalisation could be done with list comprehensions, but it would be horrible to read. And there might by Python Obex bindings, but I couldn&#8217;t&nbsp;be&nbsp;bothered.</p>

<p>I got to spend an afternoon messing with dodgy Cellphones, rather than having a teenager do the job for free. I think I chose the wrong option, but at least it&nbsp;was&nbsp;fun.</p>

<p>Footnote: Samsung, your phones User Interface is <em>awful</em>. Why on earth is Bluetooth under &#8220;Applications&#8221; rather than &#8220;Settings&#8221;? I searched everywhere but there, and finally googled before I&nbsp;found&nbsp;it&#8230;</p>
    ]]></summary>
    <content type="html"><![CDATA[<p>A housemate of mine got a new Samsung phone on the weekend. Being a resident geek, I offered to transfer her contacts across rather than get her sister to manually retype 500-odd&nbsp;contacts.</p>

<p>Naturally, I thought this would be a simple problem, right? I mean, everyone updates their phones every 2 years, this must be a pretty common use case. All my Sony Ericsson phones have had a &#8220;send all contacts by Bluetooth&#8221; option since the inception of Bluetooth. Naturally, it didn&#8217;t have such a feature, it only supports sending one contact at a time. (Although, to Samsung&#8217;s credit, the <em>new</em> phone will be able to do for the next&nbsp;upgrade)</p>

<p>Next option: I&#8217;ll sync old phone to laptop to new&nbsp;phone.</p>

<p>The Samsung website has a helpful Windows utility that you can download to do this, however you need the cable to link the phone to the computer. The phones needed different cables, and I had neither. My laptop with a Windows partition has had broken Bluetooth ever since <a href="/2006/12/29/acer-laptop-woes-part-7">its motherboard got replaced</a>. So that wasn&#8217;t an option. The phones don&#8217;t have <span class="caps">IRDA</span>, so there was no way to connect them with the Windows&nbsp;laptop.</p>

<p>Time to do it&nbsp;properly.</p>

<p>I tried <a href="http://wammu.eu/">wammu</a>, a python-based <a href="http://www.gammu.org/">gammu</a> <span class="caps">GUI</span>. It supported the phones via the &#8220;blueat&#8221; driver, and could browse their <span class="caps">SIM</span> cards fine, but not their internal Phonebooks. It couldn&#8217;t back them up either. A bit of poking around with gammu on the command line showed that the internal phone books are not 0-indexed (normal computer counting, 0 to n-1) or 1-indexed (normal human counting, 1 to n), but 2-indexed. Dijkstra would turn in his&nbsp;grave!</p>

<p>At this point, I could see that I was going to have to write my own, backup utility. The output of gammu was awkable, but seeing as there are good gammu-python bindings, I decided to do it in pure&nbsp;Python.</p>

<p>Reading the address book went something like&nbsp;this:</p>

<div class="geshifilter"><div class="python geshifilter-python" style="font-family:monospace;"><span class="kw1">import</span> gammu, <span class="kw3">pickle</span><br />
sm = gammu.<span class="me1">StateMachine</span><span class="br0">&#40;</span><span class="br0">&#41;</span><br />
sm.<span class="me1">ReadConfig</span><span class="br0">&#40;</span><span class="nu0">3</span>, <span class="nu0">0</span><span class="br0">&#41;</span><br />
sm.<span class="me1">Init</span><span class="br0">&#40;</span><span class="br0">&#41;</span><br />
old = <span class="br0">&#91;</span><span class="br0">&#93;</span><br />
<span class="kw1">for</span> i <span class="kw1">in</span> <span class="kw2">range</span><span class="br0">&#40;</span><span class="nu0">2</span>, <span class="nu0">587</span><span class="br0">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; old.<span class="me1">append</span><span class="br0">&#40;</span>sm.<span class="me1">GetMemory</span><span class="br0">&#40;</span><span class="st0">&quot;<span class="caps">ME</span>&quot;</span>, i<span class="br0">&#41;</span><span class="br0">&#41;</span><br />
<br />
<span class="kw3">pickle</span>.<span class="me1">dump</span><span class="br0">&#40;</span>old, <span class="kw2">file</span><span class="br0">&#40;</span><span class="st0">&quot;phonebook.dump&quot;</span>, <span class="st0">&quot;w&quot;</span><span class="br0">&#41;</span><span class="br0">&#41;</span></div></div>

<p>The 3 signifies gammu configuration number 3, read into position 0. 587 is the number of address book entries. &#8220;<span class="caps">ME</span>&#8221; means internal memory. I then pickled &#8220;old&#8221; in preparation for the next stage. Here is an example of an item in&nbsp;old:</p>

<div class="geshifilter"><div class="python geshifilter-python" style="font-family:monospace;"><span class="br0">&#123;</span><span class="st0">&#8216;Entries&#8217;</span>: <span class="br0">&#91;</span><span class="br0">&#123;</span><span class="st0">&#8216;AddError&#8217;</span>: <span class="nu0">7517792</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">&#8216;Type&#8217;</span>: <span class="st0">&#8216;Text_FirstName&#8217;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">&#8216;Value&#8217;</span>: u<span class="st0">&#8216;Foo&#8217;</span><span class="br0">&#125;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="br0">&#123;</span><span class="st0">&#8216;AddError&#8217;</span>: <span class="nu0">796160623</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">&#8216;Type&#8217;</span>: <span class="st0">&#8216;Text_LastName&#8217;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">&#8216;Value&#8217;</span>: u<span class="st0">&#8216;Bar&#8217;</span><span class="br0">&#125;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="br0">&#123;</span><span class="st0">&#8216;AddError&#8217;</span>: <span class="nu0">796160623</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">&#8216;SMSList&#8217;</span>: <span class="br0">&#91;</span><span class="br0">&#93;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">&#8216;Type&#8217;</span>: <span class="st0">&#8216;Number_Other&#8217;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">&#8216;Value&#8217;</span>: u<span class="st0">&#8216;0211234567&#8217;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">&#8216;VoiceTag&#8217;</span>: <span class="nu0">0</span><span class="br0">&#125;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="br0">&#123;</span><span class="st0">&#8216;Type&#8217;</span>: <span class="st0">&#8216;Category&#8217;</span>, <span class="st0">&#8216;Value&#8217;</span>: <span class="nu0">0</span><span class="br0">&#125;</span><span class="br0">&#93;</span>,<br />
&nbsp;<span class="st0">&#8216;Location&#8217;</span>: <span class="nu0">2</span>,<br />
&nbsp;<span class="st0">&#8216;MemoryType&#8217;</span>: <span class="st0">&#8216;<span class="caps">ME</span>&#8217;</span><span class="br0">&#125;</span></div></div>

<p>Pretty icky, but at least all the information is there. At this point, one should be able to feed it into the new&nbsp;phone:</p>

<div class="geshifilter"><div class="python geshifilter-python" style="font-family:monospace;">sm.<span class="me1">Terminate</span><span class="br0">&#40;</span><span class="br0">&#41;</span><br />
sm = gammu.<span class="me1">StateMachine</span><span class="br0">&#40;</span><span class="br0">&#41;</span><br />
sm.<span class="me1">ReadConfig</span><span class="br0">&#40;</span><span class="nu0">4</span>, <span class="nu0">0</span><span class="br0">&#41;</span><br />
sm.<span class="me1">Init</span><span class="br0">&#40;</span><span class="br0">&#41;</span><br />
<span class="kw1">for</span> i <span class="kw1">in</span> old:<br />
&nbsp; &nbsp; &nbsp; &nbsp; sm.<span class="me1">AddMemory</span><span class="br0">&#40;</span>i<span class="br0">&#41;</span></div></div>

<p>However nothing I tried worked, I always got an &#8220;Invalid Location&#8221; error. I think the 2-indexing is trumping gammu&nbsp;again.</p>

<p>Next idea, lets munge the data into vCard format and use wammu / gammu&#8217;s &#8220;import from vCard&#8221; function. (Code coming up soon) Turns out this doesn&#8217;t work either. The phone only received the First name, first phone number, and various other things that I didn&#8217;t send it (i.e. custom ring tones that it made up).&nbsp;Hmph!</p>

<p>Aha, but cellphones can normally Bluetooth vCards to each other. So I pushed it the vCard collection via obexftp. Starts transmitting, but then the phone reboots. I played around a bit, and found that if you send it more than one vCard in a vCard file, it reboots.&nbsp;Lovely.</p>

<p>So my final solution was: Extract address book with python-gammu. Transform into vCards. Send each one individually. At least the phone had a &#8220;trust this device&#8221; option so that it wouldn&#8217;t prompt the user for every vCard I sent, but just automatically import them - the first sensible feature I&#8217;ve found on&nbsp;it.</p>

<p>Here&nbsp;goes:</p>

<div class="geshifilter"><div class="python geshifilter-python" style="font-family:monospace;"><span class="co1">#!/usr/bin/env python</span><br />
<span class="kw1">import</span> <span class="kw3">os</span>, <span class="kw3">pickle</span>, <span class="kw3">time</span><br />
<br />
<span class="kw1">def</span> normalise_num<span class="br0">&#40;</span>n<span class="br0">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">&quot;Neaten up the phone number, internationalise, etc.&quot;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> n.<span class="me1">startswith</span><span class="br0">&#40;</span><span class="st0">&quot;+&quot;</span><span class="br0">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> n<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> n.<span class="me1">startswith</span><span class="br0">&#40;</span><span class="st0">&quot;00&quot;</span><span class="br0">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> <span class="st0">&quot;+&quot;</span> + n<span class="br0">&#91;</span><span class="nu0">2</span>:<span class="br0">&#93;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="kw2">len</span><span class="br0">&#40;</span>n<span class="br0">&#41;</span> == <span class="nu0">10</span> <span class="kw1">and</span> n<span class="br0">&#91;</span><span class="nu0">0</span><span class="br0">&#93;</span> == <span class="st0">&quot;0&quot;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> <span class="st0">&quot;+27&quot;</span> + n<span class="br0">&#91;</span><span class="nu0">1</span>:<span class="br0">&#93;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> n<br />
<br />
d = <span class="kw3">pickle</span>.<span class="me1">load</span><span class="br0">&#40;</span><span class="kw2">file</span><span class="br0">&#40;</span><span class="st0">&quot;phonebook.dump&quot;</span>, <span class="st0">&quot;r&quot;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><br />
<br />
<span class="co1"># Normalise into a sensible format:</span><br />
o = <span class="br0">&#91;</span><span class="br0">&#93;</span><br />
<span class="kw1">for</span> i <span class="kw1">in</span> d:<br />
&nbsp; &nbsp; &nbsp; &nbsp; t = <span class="br0">&#123;</span><span class="br0">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">for</span> j <span class="kw1">in</span> i<span class="br0">&#91;</span><span class="st0">&quot;Entries&quot;</span><span class="br0">&#93;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> j<span class="br0">&#91;</span><span class="st0">&quot;Type&quot;</span><span class="br0">&#93;</span> == <span class="st0">&quot;Text_FirstName&quot;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; t<span class="br0">&#91;</span><span class="st0">&quot;First&quot;</span><span class="br0">&#93;</span> = j<span class="br0">&#91;</span><span class="st0">&quot;Value&quot;</span><span class="br0">&#93;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> j<span class="br0">&#91;</span><span class="st0">&quot;Type&quot;</span><span class="br0">&#93;</span> == <span class="st0">&quot;Text_LastName&quot;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; t<span class="br0">&#91;</span><span class="st0">&quot;Last&quot;</span><span class="br0">&#93;</span> = j<span class="br0">&#91;</span><span class="st0">&quot;Value&quot;</span><span class="br0">&#93;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> j<span class="br0">&#91;</span><span class="st0">&quot;Type&quot;</span><span class="br0">&#93;</span> == <span class="st0">&quot;Number_Other&quot;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; n = normalise_num<span class="br0">&#40;</span>j<span class="br0">&#91;</span><span class="st0">&quot;Value&quot;</span><span class="br0">&#93;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">type</span> = <span class="st0">&quot;Home&quot;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> n<span class="br0">&#91;</span><span class="nu0">3</span><span class="br0">&#93;</span> <span class="kw1">in</span> <span class="br0">&#40;</span><span class="st0">&quot;7&quot;</span>, <span class="st0">&quot;8&quot;</span><span class="br0">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">type</span> = <span class="st0">&quot;Cell&quot;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="kw2">type</span> <span class="kw1">not</span> <span class="kw1">in</span> t:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; t<span class="br0">&#91;</span><span class="kw2">type</span><span class="br0">&#93;</span> = <span class="br0">&#91;</span><span class="br0">&#93;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; t<span class="br0">&#91;</span><span class="kw2">type</span><span class="br0">&#93;</span>.<span class="me1">append</span><span class="br0">&#40;</span>n<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; o.<span class="me1">append</span><span class="br0">&#40;</span>t<span class="br0">&#41;</span><br />
<br />
<span class="co1"># Write <span class="amp">&amp;</span> Send vCards:</span><br />
<span class="kw1">for</span> i <span class="kw1">in</span> o:<br />
&nbsp; &nbsp; &nbsp; &nbsp; f = <span class="kw2">file</span><span class="br0">&#40;</span><span class="st0">&quot;temp.vcf&quot;</span>, <span class="st0">&quot;w&quot;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; f.<span class="me1">write</span><span class="br0">&#40;</span><span class="st0">&quot;<span class="caps">BEGIN</span>:<span class="caps">VCARD</span><span class="es0">\n</span>&quot;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; f.<span class="me1">write</span><span class="br0">&#40;</span><span class="st0">&quot;<span class="caps">VERSION</span>:2.1<span class="es0">\n</span>&quot;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; f.<span class="me1">write</span><span class="br0">&#40;</span><span class="st0">&quot;N:%s;%s;;;<span class="es0">\n</span>&quot;</span> <span class="sy0">%</span> <span class="br0">&#40;</span>i.<span class="me1">get</span><span class="br0">&#40;</span><span class="st0">&quot;Last&quot;</span>, <span class="st0">&quot;&quot;</span><span class="br0">&#41;</span>, i.<span class="me1">get</span><span class="br0">&#40;</span><span class="st0">&quot;First&quot;</span>, <span class="st0">&quot;&quot;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; pref = <span class="st0">&quot;;<span class="caps">PREF</span>&quot;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">for</span> j <span class="kw1">in</span> i<span class="br0">&#91;</span><span class="st0">&quot;Cell&quot;</span><span class="br0">&#93;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; f.<span class="me1">write</span><span class="br0">&#40;</span><span class="st0">&quot;<span class="caps">TEL</span>;<span class="caps">CELL</span>%s:%s<span class="es0">\n</span>&quot;</span> <span class="sy0">%</span> <span class="br0">&#40;</span>pref, j<span class="br0">&#41;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pref=<span class="st0">&quot;&quot;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">for</span> j <span class="kw1">in</span> i<span class="br0">&#91;</span><span class="st0">&quot;Home&quot;</span><span class="br0">&#93;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; f.<span class="me1">write</span><span class="br0">&#40;</span><span class="st0">&quot;<span class="caps">TEL</span>;<span class="caps">HOME</span>%s:%s<span class="es0">\n</span>&quot;</span> <span class="sy0">%</span> <span class="br0">&#40;</span>pref, j<span class="br0">&#41;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pref=<span class="st0">&quot;&quot;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; f.<span class="me1">write</span><span class="br0">&#40;</span><span class="st0">&quot;<span class="caps">END</span>:<span class="caps">VCARD</span><span class="es0">\n</span>&quot;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; f.<span class="me1">close</span><span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">os</span>.<span class="me1">system</span><span class="br0">&#40;</span><span class="st0">&quot;obexftp -b 00:<span class="caps">DE</span>:<span class="caps">AD</span>:00:<span class="caps">BE</span>:<span class="caps">EF</span> -p temp.vcf&quot;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1"># Give the thing a chance to recover:</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">time</span>.<span class="me1">sleep</span><span class="br0">&#40;</span><span class="nu0">0.1</span><span class="br0">&#41;</span></div></div>

<p>Yes, the normalisation could be done with list comprehensions, but it would be horrible to read. And there might by Python Obex bindings, but I couldn&#8217;t be&nbsp;bothered.</p>

<p>I got to spend an afternoon messing with dodgy Cellphones, rather than having a teenager do the job for free. I think I chose the wrong option, but at least it was&nbsp;fun.</p>

<p>Footnote: Samsung, your phones User Interface is <em>awful</em>. Why on earth is Bluetooth under &#8220;Applications&#8221; rather than &#8220;Settings&#8221;? I searched everywhere but there, and finally googled before I found&nbsp;it&#8230;</p>
    ]]></content>
  </entry>
  <entry>
    <title>Bandwidth accounting with ulogd</title>
    <link rel="alternate" type="text/html" href="http://tumbleweed.org.za/2008/04/02/bandwidth-accounting-ulogd" />
    <id>http://tumbleweed.org.za/2008/04/02/bandwidth-accounting-ulogd</id>
    <published>2008-04-02T22:31:36+00:00</published>
    <updated>2008-09-19T00:06:06+00:00</updated>
    <author>
      <name>tumbleweed</name>
    </author>
    <category term="accounting" />
    <category term="debian" />
    <category term="iptables" />
    <category term="mysql" />
    <category term="technical" />
    <category term="traffic" />
    <category term="ulogd" />
    <summary type="html"><![CDATA[<p>My <a href="/2008/04/02/my-first-real-debian-repo">post about repositories</a> wasn&#8217;t just a <em>little</em> attempt to stave off work, it was part of a&nbsp;larger&nbsp;scheme.</p>

<p>I share the <span class="caps"><span class="caps">ADSL</span></span> line in my digs with 3 other people. We do <a href="/local-only-dsl">split-routing</a> to save money, but we still have to divide the phone bill at the end of the month. Rather than buy a fixed cap, and have a fight over who&#8217;s fault it was when we get capped, we are running a pay-per-use system (with local use free, subsidised by me). It means you don&#8217;t have to restrain yourself for the common cap, but it also means I need to calculate who&nbsp;owes&nbsp;what.</p>

<p>For the first month, I used my old standby, <a href="http://bandwidthd.sourceforge.net/">bandwidthd</a>. It uses pcap to count traffic, and gives you totals and graphs. For simplicity of logging, I gave each person a /28 for their machines and configured static <span class="caps"><span class="caps">DHCP</span></span> leases. Then bandwidthd totalled up the internet use for&nbsp;each&nbsp;/28.</p>

<p>This was sub-optimal. bandwidthd either sees the local network, in which case it can&#8217;t see which packets went out over which link. Or it can watch the international link, but then not know which user&nbsp;is&nbsp;responsible.</p>

<p>I could have installed some netflow utilities at this point, but I wanted to roll my own with the correct Linux approach (ulog) rather than any pcapping. <a href="http://www.netfilter.org/projects/ulogd/index.html">ulogd</a> is the easy&nbsp;ulog&nbsp;solution.</p>

<p>Ulogd can pick up packets that you &#8220;-j <span class="caps"><span class="caps">ULOG</span></span>&#8221; from iptables. It receives them over a netlink interface. You can tell iptables how many bytes of each packet to send, and how many to queue up before sending&nbsp;them.&nbsp;E.g.</p>

<div class="geshifilter"><div class="text geshifilter-text" style="font-family:monospace;"># iptables -I <span class="caps"><span class="caps">INPUT</span></span> 1 -j <span class="caps"><span class="caps">ULOG</span></span> &#8212;ulog-nlgroup 1 &#8212;ulog-qthreshold 50 &#8212;ulog-cprange 48 &#8212;ulog-prefix input</div></div>

<p>will log the first 48 bytes of any incoming packet to netlink-group 1. It will tag the packet as being &#8220;input&#8221;, and send them in batches of 50. 48 bytes is usually enough to catch any data you could want from the headers. If you were only need size, 4 bytes will do, and for source and destination as&nbsp;well,&nbsp;20.</p>

<p>Now, we tell ulogd to listen for this stuff and log it. Ulogd has a pluggable architecture. IPv4 decoding is a plugin, and there are various logging plugins for &#8220;-j <span class="caps"><span class="caps">LOG</span></span>&#8221; emulation, Text files, pcap-files, MySQL, PostgreSQL, and SQLite. For my purposes, I used MySQL as the router in question already had MySQL on it (for <a href="http://cacti.sourceforge.net/">Cacti</a>). Otherwise, I would have opted for SQLite. Be warned that the etch version of ulogd doesn&#8217;t automatically reconnect to the MySQL server should the connection break for any reason. I <a href="http://mirrors.tumbleweed.org.za/sr-backports/pool/main/u/ulogd/">backported the lenny version</a> to etch to get around that. (You also need to provide the <span class="geshifilter"><code class="geshifilter-text">reconnect</code></span> and <span class="geshifilter"><code class="geshifilter-text">connect_timeout</code></span>&nbsp;options.)</p>

<p>Besides the reconnection issue, the <span class="caps"><span class="caps">SQL</span></span> implementations are quite nice. They have a set schema, and you just need to create a table with the columns in it that you are interested in. No other configuration (beyond connection details)&nbsp;is&nbsp;necessary.</p>

<p>My&nbsp;MySQL&nbsp;table:</p>

<div class="geshifilter"><div class="mysql geshifilter-mysql" style="font-family:monospace;"><a href="http://search.mysql.com/search?site=refman-51&amp;q=CREATE&amp;lr=lang_en"><span class="kw1"><span class="caps"><span class="caps">CREATE</span></span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=TABLE&amp;lr=lang_en"><span class="kw1"><span class="caps"><span class="caps">TABLE</span></span></span></a> <span class="st0">`ulog`</span> <span class="br0">&#40;</span><br />
&nbsp; <span class="st0">`id`</span> <a href="http://search.mysql.com/search?site=refman-51&amp;q=INT&amp;lr=lang_en"><span class="kw4">int</span></a><span class="br0">&#40;</span><span class="nu0">10</span><span class="br0">&#41;</span> <a href="http://search.mysql.com/search?site=refman-51&amp;q=UNSIGNED&amp;lr=lang_en"><span class="kw6">unsigned</span></a> <a href="http://dev.mysql.com/doc/refman/5.1/en/non-typed-operators.html"><span class="kw10"><span class="caps"><span class="caps">NOT</span></span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=NULL&amp;lr=lang_en"><span class="kw3"><span class="caps"><span class="caps">NULL</span></span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=AUTO_INCREMENT&amp;lr=lang_en"><span class="kw6">auto_increment</span></a><span class="sy2">,</span><br />
&nbsp; <span class="st0">`oob<span class="es1">_</span>time<span class="es1">_</span>sec`</span> <a href="http://search.mysql.com/search?site=refman-51&amp;q=INT&amp;lr=lang_en"><span class="kw4">int</span></a><span class="br0">&#40;</span><span class="nu0">10</span><span class="br0">&#41;</span> <a href="http://search.mysql.com/search?site=refman-51&amp;q=UNSIGNED&amp;lr=lang_en"><span class="kw6">unsigned</span></a> <a href="http://dev.mysql.com/doc/refman/5.1/en/non-typed-operators.html"><span class="kw10"><span class="caps"><span class="caps">NOT</span></span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=NULL&amp;lr=lang_en"><span class="kw3"><span class="caps"><span class="caps">NULL</span></span></span></a><span class="sy2">,</span><br />
&nbsp; <span class="st0">`oob<span class="es1">_</span>prefix`</span> <a href="http://dev.mysql.com/doc/refman/5.1/en/string-functions.html"><span class="kw14">char</span></a><span class="br0">&#40;</span><span class="nu0">4</span><span class="br0">&#41;</span> <a href="http://dev.mysql.com/doc/refman/5.1/en/non-typed-operators.html"><span class="kw10"><span class="caps"><span class="caps">NOT</span></span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=NULL&amp;lr=lang_en"><span class="kw3"><span class="caps"><span class="caps">NULL</span></span></span></a><span class="sy2">,</span><br />
&nbsp; <span class="st0">`ip<span class="es1">_</span>totlen`</span> <a href="http://search.mysql.com/search?site=refman-51&amp;q=SMALLINT&amp;lr=lang_en"><span class="kw4">smallint</span></a><span class="br0">&#40;</span><span class="nu0">5</span><span class="br0">&#41;</span> <a href="http://search.mysql.com/search?site=refman-51&amp;q=UNSIGNED&amp;lr=lang_en"><span class="kw6">unsigned</span></a> <a href="http://dev.mysql.com/doc/refman/5.1/en/non-typed-operators.html"><span class="kw10"><span class="caps"><span class="caps">NOT</span></span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=NULL&amp;lr=lang_en"><span class="kw3"><span class="caps"><span class="caps">NULL</span></span></span></a><span class="sy2">,</span><br />
&nbsp; <a href="http://search.mysql.com/search?site=refman-51&amp;q=PRIMARY%20KEY&amp;lr=lang_en"><span class="kw1"><span class="caps"><span class="caps">PRIMARY</span></span> <span class="caps"><span class="caps">KEY</span></span></span></a> &nbsp;<span class="br0">&#40;</span><span class="st0">`id`</span><span class="br0">&#41;</span><span class="sy2">,</span><br />
&nbsp; <a href="http://search.mysql.com/search?site=refman-51&amp;q=UNIQUE&amp;lr=lang_en"><span class="kw6"><span class="caps"><span class="caps">UNIQUE</span></span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=KEY&amp;lr=lang_en"><span class="kw1"><span class="caps"><span class="caps">KEY</span></span></span></a> <span class="st0">`id`</span> <span class="br0">&#40;</span><span class="st0">`id`</span><span class="br0">&#41;</span><span class="sy2">,</span><br />
&nbsp; <a href="http://search.mysql.com/search?site=refman-51&amp;q=KEY&amp;lr=lang_en"><span class="kw1"><span class="caps"><span class="caps">KEY</span></span></span></a> <span class="st0">`oob<span class="es1">_</span>prefix`</span> <span class="br0">&#40;</span><span class="st0">`oob<span class="es1">_</span>prefix`</span><span class="br0">&#41;</span><span class="sy2">,</span><br />
&nbsp; <a href="http://search.mysql.com/search?site=refman-51&amp;q=KEY&amp;lr=lang_en"><span class="kw1"><span class="caps"><span class="caps">KEY</span></span></span></a> <span class="st0">`oob<span class="es1">_</span>time<span class="es1">_</span>sec`</span> <span class="br0">&#40;</span><span class="st0">`oob<span class="es1">_</span>time<span class="es1">_</span>sec`</span><span class="br0">&#41;</span><br />
<span class="br0">&#41;</span><span class="sy2">;</span></div></div>

<p>My&nbsp;ulogd.conf:</p>

<div class="geshifilter"><div class="ini geshifilter-ini" style="font-family:monospace;"><span class="re0"><span class="br0">&#91;</span>global<span class="br0">&#93;</span></span><br />
# netlink multicast group <span class="br0">&#40;</span>the same as the iptables &#8212;ulog-nlgroup param<span class="br0">&#41;</span><br />
<span class="re1">nlgroup</span><span class="sy0">=</span><span class="re2"><span class="nu0">1</span> &nbsp; &nbsp;</span><br />
# logfile for status messages<br />
<span class="re1">logfile</span><span class="sy0">=</span><span class="st0">&quot;/var/log/ulog/ulogd.log&quot;</span> &nbsp; &nbsp;<br />
# loglevel: debug<span class="br0">&#40;</span><span class="nu0">1</span><span class="br0">&#41;</span>, info<span class="br0">&#40;</span><span class="nu0">3</span><span class="br0">&#41;</span>, notice<span class="br0">&#40;</span><span class="nu0">5</span><span class="br0">&#41;</span>, error<span class="br0">&#40;</span><span class="nu0">7</span><span class="br0">&#41;</span> or fatal<span class="br0">&#40;</span><span class="nu0">8</span><span class="br0">&#41;</span><br />
<span class="re1">loglevel</span><span class="sy0">=</span><span class="re2"><span class="nu0">5</span> &nbsp; &nbsp;</span><br />
# socket receive buffer size <span class="br0">&#40;</span>should be at least the size of the<br />
# in-kernel buffer <span class="br0">&#40;</span>ipt_ULOG.o &#8216;nlbufsiz&#8217; parameter<span class="br0">&#41;</span><br />
<span class="re1">rmem</span><span class="sy0">=</span><span class="re2"><span class="nu0">131071</span> &nbsp; &nbsp;</span><br />
# libipulog/ulogd receive buffer size, should be &gt; rmem<br />
<span class="re1">bufsize</span><span class="sy0">=</span><span class="re2">150000</span><br />
# ulogd_BASE.so - interpreter plugin for basic IPv4 header fields<br />
# &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; you will always need this<br />
<span class="re1">plugin</span><span class="sy0">=</span><span class="st0">&quot;/usr/lib/ulogd/ulogd_BASE.so&quot;</span><br />
<span class="re1">plugin</span><span class="sy0">=</span><span class="st0">&quot;/usr/lib/ulogd/ulogd_MYSQL.so&quot;</span><br />
<br />
<span class="re0"><span class="br0">&#91;</span><span class="caps"><span class="caps">MYSQL</span></span><span class="br0">&#93;</span></span><br />
<span class="re1">table</span><span class="sy0">=</span><span class="st0">&quot;ulog&quot;</span><br />
<span class="re1">pass</span><span class="sy0">=</span><span class="st0">&quot;foo&quot;</span><br />
<span class="re1">user</span><span class="sy0">=</span><span class="st0">&quot;ulog&quot;</span><br />
<span class="re1">db</span><span class="sy0">=</span><span class="st0">&quot;ulog&quot;</span><br />
<span class="re1">host</span><span class="sy0">=</span><span class="st0">&quot;localhost&quot;</span><br />
<span class="re1">reconnect</span><span class="sy0">=</span><span class="re2">5</span><br />
<span class="re1">connect_timeout</span><span class="sy0">=</span><span class="re2">10</span></div></div>

<p>The relevant parts of my&nbsp;firewall&nbsp;rules:</p>

<div class="geshifilter"><div class="bash geshifilter-bash" style="font-family:monospace;"><span class="co0"># Count proxy usage (transparent and explicit)</span><br />
iptables <span class="re5">-A</span> count-from-inside <span class="re5">-p</span> <span class="sy0">!</span> tcp <span class="re5">-j</span> <span class="caps"><span class="caps">RETURN</span></span><br />
iptables <span class="re5">-A</span> count-from-inside <span class="re5">-p</span> tcp <span class="re5">-m</span> multiport <span class="re5">&#8212;destination-ports</span> <span class="sy0">!</span> <span class="nu0">3128</span>,<span class="nu0">8080</span> <span class="re5">-j</span> <span class="caps"><span class="caps">RETURN</span></span><br />
iptables <span class="re5">-A</span> count-from-inside <span class="re5">-s</span> 10.0.0.16<span class="sy0">/</span><span class="nu0">28</span> <span class="re5">-j</span> <span class="caps"><span class="caps">ULOG</span></span> <span class="re5">&#8212;ulog-nlgroup</span> <span class="nu0">1</span> <span class="re5">&#8212;ulog-qthreshold</span> <span class="nu0">50</span> <span class="re5">&#8212;ulog-cprange</span> <span class="nu0">4</span> <span class="re5">&#8212;ulog-prefix</span> sr-p<br />
iptables <span class="re5">-A</span> count-from-inside <span class="re5">-s</span> 10.0.0.32<span class="sy0">/</span><span class="nu0">28</span> <span class="re5">-j</span> <span class="caps"><span class="caps">ULOG</span></span> <span class="re5">&#8212;ulog-nlgroup</span> <span class="nu0">1</span> <span class="re5">&#8212;ulog-qthreshold</span> <span class="nu0">50</span> <span class="re5">&#8212;ulog-cprange</span> <span class="nu0">4</span> <span class="re5">&#8212;ulog-prefix</span> fb-p<br />
iptables <span class="re5">-A</span> count-from-inside <span class="re5">-s</span> 10.0.0.128<span class="sy0">/</span><span class="nu0">25</span> <span class="re5">-j</span> <span class="caps"><span class="caps">ULOG</span></span> <span class="re5">&#8212;ulog-nlgroup</span> <span class="nu0">1</span> <span class="re5">&#8212;ulog-qthreshold</span> <span class="nu0">50</span> <span class="re5">&#8212;ulog-cprange</span> <span class="nu0">4</span> <span class="re5">&#8212;ulog-prefix</span> gu-p<br />
<br />
iptables <span class="re5">-A</span> count-to-inside <span class="re5">-p</span> <span class="sy0">!</span> tcp <span class="re5">-j</span> <span class="caps"><span class="caps">RETURN</span></span><br />
iptables <span class="re5">-A</span> count-to-inside <span class="re5">-p</span> tcp <span class="re5">-m</span> multiport <span class="re5">&#8212;source-ports</span> <span class="sy0">!</span> <span class="nu0">3128</span>,<span class="nu0">8080</span> <span class="re5">-j</span> <span class="caps"><span class="caps">RETURN</span></span><br />
iptables <span class="re5">-A</span> count-to-inside <span class="re5">-d</span> 10.0.0.16<span class="sy0">/</span><span class="nu0">28</span> <span class="re5">-j</span> <span class="caps"><span class="caps">ULOG</span></span> <span class="re5">&#8212;ulog-nlgroup</span> <span class="nu0">1</span> <span class="re5">&#8212;ulog-qthreshold</span> <span class="nu0">50</span> <span class="re5">&#8212;ulog-cprange</span> <span class="nu0">4</span> <span class="re5">&#8212;ulog-prefix</span> sr-p<br />
iptables <span class="re5">-A</span> count-to-inside <span class="re5">-d</span> 10.0.0.32<span class="sy0">/</span><span class="nu0">28</span> <span class="re5">-j</span> <span class="caps"><span class="caps">ULOG</span></span> <span class="re5">&#8212;ulog-nlgroup</span> <span class="nu0">1</span> <span class="re5">&#8212;ulog-qthreshold</span> <span class="nu0">50</span> <span class="re5">&#8212;ulog-cprange</span> <span class="nu0">4</span> <span class="re5">&#8212;ulog-prefix</span> fb-p<br />
iptables <span class="re5">-A</span> count-to-inside <span class="re5">-d</span> 10.0.0.128<span class="sy0">/</span><span class="nu0">25</span> <span class="re5">-j</span> <span class="caps"><span class="caps">ULOG</span></span> <span class="re5">&#8212;ulog-nlgroup</span> <span class="nu0">1</span> <span class="re5">&#8212;ulog-qthreshold</span> <span class="nu0">50</span> <span class="re5">&#8212;ulog-cprange</span> <span class="nu0">4</span> <span class="re5">&#8212;ulog-prefix</span> gu-p<br />
<br />
<span class="co0"># Count forwarded traffic (excluding local internet connection - ppp2)</span><br />
iptables <span class="re5">-A</span> count-forward-in <span class="re5">-i</span> ppp2 <span class="re5">-j</span> <span class="caps"><span class="caps">RETURN</span></span><br />
iptables <span class="re5">-A</span> count-forward-in <span class="re5">-d</span> 10.0.0.16<span class="sy0">/</span><span class="nu0">28</span> <span class="re5">-j</span> <span class="caps"><span class="caps">ULOG</span></span> <span class="re5">&#8212;ulog-nlgroup</span> <span class="nu0">1</span> <span class="re5">&#8212;ulog-qthreshold</span> <span class="nu0">50</span> <span class="re5">&#8212;ulog-cprange</span> <span class="nu0">4</span> <span class="re5">&#8212;ulog-prefix</span> sr-f<br />
iptables <span class="re5">-A</span> count-forward-in <span class="re5">-d</span> 10.0.0.32<span class="sy0">/</span><span class="nu0">28</span> <span class="re5">-j</span> <span class="caps"><span class="caps">ULOG</span></span> <span class="re5">&#8212;ulog-nlgroup</span> <span class="nu0">1</span> <span class="re5">&#8212;ulog-qthreshold</span> <span class="nu0">50</span> <span class="re5">&#8212;ulog-cprange</span> <span class="nu0">4</span> <span class="re5">&#8212;ulog-prefix</span> fb-f<br />
iptables <span class="re5">-A</span> count-forward-in <span class="re5">-d</span> 10.0.0.128<span class="sy0">/</span><span class="nu0">25</span> <span class="re5">-j</span> <span class="caps"><span class="caps">ULOG</span></span> <span class="re5">&#8212;ulog-nlgroup</span> <span class="nu0">1</span> <span class="re5">&#8212;ulog-qthreshold</span> <span class="nu0">50</span> <span class="re5">&#8212;ulog-cprange</span> <span class="nu0">4</span> <span class="re5">&#8212;ulog-prefix</span> gu-f<br />
<br />
iptables <span class="re5">-A</span> count-forward-out <span class="re5">-o</span> ppp2 <span class="re5">-j</span> <span class="caps"><span class="caps">RETURN</span></span><br />
iptables <span class="re5">-A</span> count-forward-out <span class="re5">-s</span> 10.0.0.16<span class="sy0">/</span><span class="nu0">28</span> <span class="re5">-j</span> <span class="caps"><span class="caps">ULOG</span></span> <span class="re5">&#8212;ulog-nlgroup</span> <span class="nu0">1</span> <span class="re5">&#8212;ulog-qthreshold</span> <span class="nu0">50</span> <span class="re5">&#8212;ulog-cprange</span> <span class="nu0">4</span> <span class="re5">&#8212;ulog-prefix</span> sr-f<br />
iptables <span class="re5">-A</span> count-forward-out <span class="re5">-s</span> 10.0.0.32<span class="sy0">/</span><span class="nu0">28</span> <span class="re5">-j</span> <span class="caps"><span class="caps">ULOG</span></span> <span class="re5">&#8212;ulog-nlgroup</span> <span class="nu0">1</span> <span class="re5">&#8212;ulog-qthreshold</span> <span class="nu0">50</span> <span class="re5">&#8212;ulog-cprange</span> <span class="nu0">4</span> <span class="re5">&#8212;ulog-prefix</span> fb-f<br />
iptables <span class="re5">-A</span> count-forward-out <span class="re5">-s</span> 10.0.0.128<span class="sy0">/</span><span class="nu0">25</span> <span class="re5">-j</span> <span class="caps"><span class="caps">ULOG</span></span> <span class="re5">&#8212;ulog-nlgroup</span> <span class="nu0">1</span> <span class="re5">&#8212;ulog-qthreshold</span> <span class="nu0">50</span> <span class="re5">&#8212;ulog-cprange</span> <span class="nu0">4</span> <span class="re5">&#8212;ulog-prefix</span> gu-f<br />
<br />
<span class="co0"># Glue</span><br />
iptables <span class="re5">-A</span> <span class="caps"><span class="caps">INPUT</span></span> <span class="re5">-i</span> eth0 <span class="re5">-j</span> count-from-inside<br />
iptables <span class="re5">-A</span> <span class="caps"><span class="caps">OUTPUT</span></span> &nbsp;<span class="re5">-o</span> eth0 <span class="re5">-j</span> count-to-inside<br />
iptables <span class="re5">-A</span> <span class="caps"><span class="caps">FORWARD</span></span> <span class="re5">-i</span> ppp+ <span class="re5">-j</span> count-forward-in<br />
iptables <span class="re5">-A</span> <span class="caps"><span class="caps">FORWARD</span></span> <span class="re5">-o</span> ppp+ <span class="re5">-j</span> count-forward-out</div></div>

<p>So, traffic for my /28 (sr) will be counted as <span class="geshifilter"><code class="geshifilter-text">sr-f</code></span> or <span class="geshifilter"><code class="geshifilter-text">sr-p</code></span> so I can tally up proxy <span class="amp">&amp;</span> forwarded traffic separately. (Yes, I can count traffic with squid too, but doing it all in one place is simpler.) <span class="geshifilter"><code class="geshifilter-text">fb</code></span> is random housemate Foo Bar, and <span class="geshifilter"><code class="geshifilter-text">gu</code></span> guest (unreserved&nbsp;<span class="caps">IP</span>&nbsp;addresses).</p>

<p>You can query the usage this month with&nbsp;for&nbsp;example:</p>

<div class="geshifilter"><div class="mysql geshifilter-mysql" style="font-family:monospace;"><a href="http://search.mysql.com/search?site=refman-51&amp;q=SELECT&amp;lr=lang_en"><span class="kw1"><span class="caps"><span class="caps">SELECT</span></span></span></a> oob_prefix<span class="sy2">,</span> <a href="http://dev.mysql.com/doc/refman/5.1/en/group-by-functions-and-modifiers.html"><span class="kw22"><span class="caps"><span class="caps">SUM</span></span></span></a><span class="br0">&#40;</span>ip_totlen<span class="br0">&#41;</span> <a href="http://search.mysql.com/search?site=refman-51&amp;q=FROM&amp;lr=lang_en"><span class="kw1"><span class="caps"><span class="caps">FROM</span></span></span></a> ulog <a href="http://search.mysql.com/search?site=refman-51&amp;q=WHERE&amp;lr=lang_en"><span class="kw1"><span class="caps"><span class="caps">WHERE</span></span></span></a> oob_time_sec <span class="sy1">&gt;</span> <a href="http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html"><span class="kw17">UNIX_TIMESTAMP</span></a><span class="br0">&#40;</span><span class="st0">&#8216;2008-04-01 00:00:00&#8217;</span><span class="br0">&#41;</span> <a href="http://search.mysql.com/search?site=refman-51&amp;q=GROUP%20BY&amp;lr=lang_en"><span class="kw1"><span class="caps"><span class="caps">GROUP</span></span> <span class="caps"><span class="caps">BY</span></span></span></a> oob_prefix<span class="sy2">;</span></div></div>

<p>Your table will fill up fast. We are averaging around 200 000 rows per day. So obviously some caching is&nbsp;in&nbsp;order:</p>

<div class="geshifilter"><div class="mysql geshifilter-mysql" style="font-family:monospace;"><a href="http://search.mysql.com/search?site=refman-51&amp;q=CREATE&amp;lr=lang_en"><span class="kw1"><span class="caps"><span class="caps">CREATE</span></span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=TABLE&amp;lr=lang_en"><span class="kw1"><span class="caps"><span class="caps">TABLE</span></span></span></a> daily <span class="br0">&#40;</span><br />
&nbsp; id <a href="http://search.mysql.com/search?site=refman-51&amp;q=INT&amp;lr=lang_en"><span class="kw4"><span class="caps"><span class="caps">INT</span></span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=UNSIGNED&amp;lr=lang_en"><span class="kw6"><span class="caps"><span class="caps">UNSIGNED</span></span></span></a> <a href="http://dev.mysql.com/doc/refman/5.1/en/non-typed-operators.html"><span class="kw10"><span class="caps"><span class="caps">NOT</span></span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=NULL&amp;lr=lang_en"><span class="kw3"><span class="caps"><span class="caps">NULL</span></span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=AUTO_INCREMENT&amp;lr=lang_en"><span class="kw6">AUTO_INCREMENT</span></a><span class="sy2">,</span><br />
&nbsp; <a href="http://search.mysql.com/search?site=refman-51&amp;q=TIME&amp;lr=lang_en"><span class="kw5">time</span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=TIMESTAMP&amp;lr=lang_en"><span class="kw4"><span class="caps"><span class="caps">TIMESTAMP</span></span></span></a><span class="sy2">,</span><br />
&nbsp; oob_prefix <a href="http://dev.mysql.com/doc/refman/5.1/en/string-functions.html"><span class="kw14"><span class="caps"><span class="caps">CHAR</span></span></span></a><span class="br0">&#40;</span><span class="nu0">4</span><span class="br0">&#41;</span> <a href="http://dev.mysql.com/doc/refman/5.1/en/non-typed-operators.html"><span class="kw10"><span class="caps"><span class="caps">NOT</span></span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=NULL&amp;lr=lang_en"><span class="kw3"><span class="caps"><span class="caps">NULL</span></span></span></a><span class="sy2">,</span><br />
&nbsp; <a href="http://search.mysql.com/search?site=refman-51&amp;q=DATA&amp;lr=lang_en"><span class="kw1">data</span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=INT&amp;lr=lang_en"><span class="kw4"><span class="caps"><span class="caps">INT</span></span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=UNSIGNED&amp;lr=lang_en"><span class="kw6"><span class="caps"><span class="caps">UNSIGNED</span></span></span></a> <a href="http://dev.mysql.com/doc/refman/5.1/en/non-typed-operators.html"><span class="kw10"><span class="caps"><span class="caps">NOT</span></span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=NULL&amp;lr=lang_en"><span class="kw3"><span class="caps"><span class="caps">NULL</span></span></span></a><span class="sy2">,</span><br />
&nbsp; <a href="http://search.mysql.com/search?site=refman-51&amp;q=PRIMARY%20KEY&amp;lr=lang_en"><span class="kw1"><span class="caps"><span class="caps">PRIMARY</span></span> <span class="caps"><span class="caps">KEY</span></span></span></a> <span class="br0">&#40;</span>id<span class="br0">&#41;</span><span class="sy2">,</span><br />
&nbsp; <a href="http://search.mysql.com/search?site=refman-51&amp;q=KEY&amp;lr=lang_en"><span class="kw1"><span class="caps"><span class="caps">KEY</span></span></span></a> <span class="br0">&#40;</span>oob_prefix<span class="br0">&#40;</span><span class="nu0">4</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy2">,</span><br />
&nbsp; <a href="http://search.mysql.com/search?site=refman-51&amp;q=KEY&amp;lr=lang_en"><span class="kw1"><span class="caps"><span class="caps">KEY</span></span></span></a> <span class="br0">&#40;</span><a href="http://search.mysql.com/search?site=refman-51&amp;q=TIME&amp;lr=lang_en"><span class="kw5">time</span></a><span class="br0">&#41;</span><br />
<span class="br0">&#41;</span><span class="sy2">;</span></div></div>

<p>And every night, run&nbsp;something&nbsp;like:</p>

<div class="geshifilter"><div class="mysql geshifilter-mysql" style="font-family:monospace;"><a href="http://search.mysql.com/search?site=refman-51&amp;q=INSERT&amp;lr=lang_en"><span class="kw2"><span class="caps"><span class="caps">INSERT</span></span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=INTO&amp;lr=lang_en"><span class="kw1"><span class="caps"><span class="caps">INTO</span></span></span></a> daily <span class="br0">&#40;</span><a href="http://search.mysql.com/search?site=refman-51&amp;q=TIME&amp;lr=lang_en"><span class="kw5">time</span></a><span class="sy2">,</span> oob_prefix<span class="sy2">,</span> <a href="http://search.mysql.com/search?site=refman-51&amp;q=DATA&amp;lr=lang_en"><span class="kw1">data</span></a><span class="br0">&#41;</span><br />
<a href="http://search.mysql.com/search?site=refman-51&amp;q=SELECT&amp;lr=lang_en"><span class="kw1"><span class="caps"><span class="caps">SELECT</span></span></span></a> <a href="http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html"><span class="kw17">FROM_UNIXTIME</span></a><span class="br0">&#40;</span><a href="http://dev.mysql.com/doc/refman/5.1/en/group-by-functions-and-modifiers.html"><span class="kw22"><span class="caps"><span class="caps">MAX</span></span></span></a><span class="br0">&#40;</span>oob_time_sec<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy2">,</span> oob_prefix<span class="sy2">,</span> <a href="http://dev.mysql.com/doc/refman/5.1/en/group-by-functions-and-modifiers.html"><span class="kw22"><span class="caps"><span class="caps">SUM</span></span></span></a><span class="br0">&#40;</span>ip_totlen<span class="br0">&#41;</span><br />
<a href="http://search.mysql.com/search?site=refman-51&amp;q=FROM&amp;lr=lang_en"><span class="kw1"><span class="caps"><span class="caps">FROM</span></span></span></a> ulog<br />
<a href="http://search.mysql.com/search?site=refman-51&amp;q=WHERE&amp;lr=lang_en"><span class="kw1"><span class="caps"><span class="caps">WHERE</span></span></span></a> oob_time_sec <span class="sy1">&gt;=</span> <a href="http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html"><span class="kw17">UNIX_TIMESTAMP</span></a><span class="br0">&#40;</span><span class="st0">&#8216;2008-04-01 00:00:00&#8217;</span><span class="br0">&#41;</span><br />
&nbsp; <a href="http://dev.mysql.com/doc/refman/5.1/en/non-typed-operators.html"><span class="kw10"><span class="caps"><span class="caps">AND</span></span></span></a> oob_time_sec <span class="sy1">&lt;</span> <a href="http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html"><span class="kw17">UNIX_TIMESTAMP</span></a><span class="br0">&#40;</span><span class="st0">&#8216;2008-04-02 00:00:00&#8217;</span><span class="br0">&#41;</span><br />
<a href="http://search.mysql.com/search?site=refman-51&amp;q=GROUP%20BY&amp;lr=lang_en"><span class="kw1"><span class="caps"><span class="caps">GROUP</span></span> <span class="caps"><span class="caps">BY</span></span></span></a> oob_prefix<span class="sy2">;</span><br />
<a href="http://search.mysql.com/search?site=refman-51&amp;q=DELETE&amp;lr=lang_en"><span class="kw1"><span class="caps"><span class="caps">DELETE</span></span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=FROM&amp;lr=lang_en"><span class="kw1"><span class="caps"><span class="caps">FROM</span></span></span></a> ulog <a href="http://search.mysql.com/search?site=refman-51&amp;q=WHERE&amp;lr=lang_en"><span class="kw1"><span class="caps"><span class="caps">WHERE</span></span></span></a> oob_time_sec &nbsp;<span class="sy1">&gt;=</span> <a href="http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html"><span class="kw17">UNIX_TIMESTAMP</span></a><span class="br0">&#40;</span><span class="st0">&#8216;2008-04-01 00:00:00&#8217;</span><span class="br0">&#41;</span><br />
&nbsp; <a href="http://dev.mysql.com/doc/refman/5.1/en/non-typed-operators.html"><span class="kw10"><span class="caps"><span class="caps">AND</span></span></span></a> oob_time_sec <span class="sy1">&lt;</span> <a href="http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html"><span class="kw17">UNIX_TIMESTAMP</span></a><span class="br0">&#40;</span><span class="st0">&#8216;2008-04-02 00:00:00&#8217;</span><span class="br0">&#41;</span><span class="sy2">;</span></div></div>

<p>Finally, I have a simple little <span class="caps"><span class="caps">PHP</span></span> script that provides reporting and calculates&nbsp;dues.&nbsp;Done.</p>
    ]]></summary>
    <content type="html"><![CDATA[<p>My <a href="/2008/04/02/my-first-real-debian-repo">post about repositories</a> wasn&#8217;t just a <em>little</em> attempt to stave off work, it was part of a larger&nbsp;scheme.</p>

<p>I share the <span class="caps">ADSL</span> line in my digs with 3 other people. We do <a href="/local-only-dsl">split-routing</a> to save money, but we still have to divide the phone bill at the end of the month. Rather than buy a fixed cap, and have a fight over who&#8217;s fault it was when we get capped, we are running a pay-per-use system (with local use free, subsidised by me). It means you don&#8217;t have to restrain yourself for the common cap, but it also means I need to calculate who owes&nbsp;what.</p>

<p>For the first month, I used my old standby, <a href="http://bandwidthd.sourceforge.net/">bandwidthd</a>. It uses pcap to count traffic, and gives you totals and graphs. For simplicity of logging, I gave each person a /28 for their machines and configured static <span class="caps">DHCP</span> leases. Then bandwidthd totalled up the internet use for each&nbsp;/28.</p>

<p>This was sub-optimal. bandwidthd either sees the local network, in which case it can&#8217;t see which packets went out over which link. Or it can watch the international link, but then not know which user is&nbsp;responsible.</p>

<p>I could have installed some netflow utilities at this point, but I wanted to roll my own with the correct Linux approach (ulog) rather than any pcapping. <a href="http://www.netfilter.org/projects/ulogd/index.html">ulogd</a> is the easy ulog&nbsp;solution.</p>

<p>Ulogd can pick up packets that you &#8220;-j <span class="caps">ULOG</span>&#8221; from iptables. It receives them over a netlink interface. You can tell iptables how many bytes of each packet to send, and how many to queue up before sending them.&nbsp;E.g.</p>

<div class="geshifilter"><div class="text geshifilter-text" style="font-family:monospace;"># iptables -I <span class="caps">INPUT</span> 1 -j <span class="caps">ULOG</span> &#8212;ulog-nlgroup 1 &#8212;ulog-qthreshold 50 &#8212;ulog-cprange 48 &#8212;ulog-prefix input</div></div>

<p>will log the first 48 bytes of any incoming packet to netlink-group 1. It will tag the packet as being &#8220;input&#8221;, and send them in batches of 50. 48 bytes is usually enough to catch any data you could want from the headers. If you were only need size, 4 bytes will do, and for source and destination as well,&nbsp;20.</p>

<p>Now, we tell ulogd to listen for this stuff and log it. Ulogd has a pluggable architecture. IPv4 decoding is a plugin, and there are various logging plugins for &#8220;-j <span class="caps">LOG</span>&#8221; emulation, Text files, pcap-files, MySQL, PostgreSQL, and SQLite. For my purposes, I used MySQL as the router in question already had MySQL on it (for <a href="http://cacti.sourceforge.net/">Cacti</a>). Otherwise, I would have opted for SQLite. Be warned that the etch version of ulogd doesn&#8217;t automatically reconnect to the MySQL server should the connection break for any reason. I <a href="http://mirrors.tumbleweed.org.za/sr-backports/pool/main/u/ulogd/">backported the lenny version</a> to etch to get around that. (You also need to provide the <code>reconnect</code> and <code>connect_timeout</code>&nbsp;options.)</p>

<p>Besides the reconnection issue, the <span class="caps">SQL</span> implementations are quite nice. They have a set schema, and you just need to create a table with the columns in it that you are interested in. No other configuration (beyond connection details) is&nbsp;necessary.</p>

<p>My MySQL&nbsp;table:</p>

<div class="geshifilter"><div class="mysql geshifilter-mysql" style="font-family:monospace;"><a href="http://search.mysql.com/search?site=refman-51&amp;q=CREATE&amp;lr=lang_en"><span class="kw1"><span class="caps">CREATE</span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=TABLE&amp;lr=lang_en"><span class="kw1"><span class="caps">TABLE</span></span></a> <span class="st0">`ulog`</span> <span class="br0">&#40;</span><br />
&nbsp; <span class="st0">`id`</span> <a href="http://search.mysql.com/search?site=refman-51&amp;q=INT&amp;lr=lang_en"><span class="kw4">int</span></a><span class="br0">&#40;</span><span class="nu0">10</span><span class="br0">&#41;</span> <a href="http://search.mysql.com/search?site=refman-51&amp;q=UNSIGNED&amp;lr=lang_en"><span class="kw6">unsigned</span></a> <a href="http://dev.mysql.com/doc/refman/5.1/en/non-typed-operators.html"><span class="kw10"><span class="caps">NOT</span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=NULL&amp;lr=lang_en"><span class="kw3"><span class="caps">NULL</span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=AUTO_INCREMENT&amp;lr=lang_en"><span class="kw6">auto_increment</span></a><span class="sy2">,</span><br />
&nbsp; <span class="st0">`oob<span class="es1">_</span>time<span class="es1">_</span>sec`</span> <a href="http://search.mysql.com/search?site=refman-51&amp;q=INT&amp;lr=lang_en"><span class="kw4">int</span></a><span class="br0">&#40;</span><span class="nu0">10</span><span class="br0">&#41;</span> <a href="http://search.mysql.com/search?site=refman-51&amp;q=UNSIGNED&amp;lr=lang_en"><span class="kw6">unsigned</span></a> <a href="http://dev.mysql.com/doc/refman/5.1/en/non-typed-operators.html"><span class="kw10"><span class="caps">NOT</span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=NULL&amp;lr=lang_en"><span class="kw3"><span class="caps">NULL</span></span></a><span class="sy2">,</span><br />
&nbsp; <span class="st0">`oob<span class="es1">_</span>prefix`</span> <a href="http://dev.mysql.com/doc/refman/5.1/en/string-functions.html"><span class="kw14">char</span></a><span class="br0">&#40;</span><span class="nu0">4</span><span class="br0">&#41;</span> <a href="http://dev.mysql.com/doc/refman/5.1/en/non-typed-operators.html"><span class="kw10"><span class="caps">NOT</span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=NULL&amp;lr=lang_en"><span class="kw3"><span class="caps">NULL</span></span></a><span class="sy2">,</span><br />
&nbsp; <span class="st0">`ip<span class="es1">_</span>totlen`</span> <a href="http://search.mysql.com/search?site=refman-51&amp;q=SMALLINT&amp;lr=lang_en"><span class="kw4">smallint</span></a><span class="br0">&#40;</span><span class="nu0">5</span><span class="br0">&#41;</span> <a href="http://search.mysql.com/search?site=refman-51&amp;q=UNSIGNED&amp;lr=lang_en"><span class="kw6">unsigned</span></a> <a href="http://dev.mysql.com/doc/refman/5.1/en/non-typed-operators.html"><span class="kw10"><span class="caps">NOT</span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=NULL&amp;lr=lang_en"><span class="kw3"><span class="caps">NULL</span></span></a><span class="sy2">,</span><br />
&nbsp; <a href="http://search.mysql.com/search?site=refman-51&amp;q=PRIMARY%20KEY&amp;lr=lang_en"><span class="kw1"><span class="caps">PRIMARY</span> <span class="caps">KEY</span></span></a> &nbsp;<span class="br0">&#40;</span><span class="st0">`id`</span><span class="br0">&#41;</span><span class="sy2">,</span><br />
&nbsp; <a href="http://search.mysql.com/search?site=refman-51&amp;q=UNIQUE&amp;lr=lang_en"><span class="kw6"><span class="caps">UNIQUE</span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=KEY&amp;lr=lang_en"><span class="kw1"><span class="caps">KEY</span></span></a> <span class="st0">`id`</span> <span class="br0">&#40;</span><span class="st0">`id`</span><span class="br0">&#41;</span><span class="sy2">,</span><br />
&nbsp; <a href="http://search.mysql.com/search?site=refman-51&amp;q=KEY&amp;lr=lang_en"><span class="kw1"><span class="caps">KEY</span></span></a> <span class="st0">`oob<span class="es1">_</span>prefix`</span> <span class="br0">&#40;</span><span class="st0">`oob<span class="es1">_</span>prefix`</span><span class="br0">&#41;</span><span class="sy2">,</span><br />
&nbsp; <a href="http://search.mysql.com/search?site=refman-51&amp;q=KEY&amp;lr=lang_en"><span class="kw1"><span class="caps">KEY</span></span></a> <span class="st0">`oob<span class="es1">_</span>time<span class="es1">_</span>sec`</span> <span class="br0">&#40;</span><span class="st0">`oob<span class="es1">_</span>time<span class="es1">_</span>sec`</span><span class="br0">&#41;</span><br />
<span class="br0">&#41;</span><span class="sy2">;</span></div></div>

<p>My&nbsp;ulogd.conf:</p>

<div class="geshifilter"><div class="ini geshifilter-ini" style="font-family:monospace;"><span class="re0"><span class="br0">&#91;</span>global<span class="br0">&#93;</span></span><br />
# netlink multicast group <span class="br0">&#40;</span>the same as the iptables &#8212;ulog-nlgroup param<span class="br0">&#41;</span><br />
<span class="re1">nlgroup</span><span class="sy0">=</span><span class="re2"><span class="nu0">1</span> &nbsp; &nbsp;</span><br />
# logfile for status messages<br />
<span class="re1">logfile</span><span class="sy0">=</span><span class="st0">&quot;/var/log/ulog/ulogd.log&quot;</span> &nbsp; &nbsp;<br />
# loglevel: debug<span class="br0">&#40;</span><span class="nu0">1</span><span class="br0">&#41;</span>, info<span class="br0">&#40;</span><span class="nu0">3</span><span class="br0">&#41;</span>, notice<span class="br0">&#40;</span><span class="nu0">5</span><span class="br0">&#41;</span>, error<span class="br0">&#40;</span><span class="nu0">7</span><span class="br0">&#41;</span> or fatal<span class="br0">&#40;</span><span class="nu0">8</span><span class="br0">&#41;</span><br />
<span class="re1">loglevel</span><span class="sy0">=</span><span class="re2"><span class="nu0">5</span> &nbsp; &nbsp;</span><br />
# socket receive buffer size <span class="br0">&#40;</span>should be at least the size of the<br />
# in-kernel buffer <span class="br0">&#40;</span>ipt_ULOG.o &#8216;nlbufsiz&#8217; parameter<span class="br0">&#41;</span><br />
<span class="re1">rmem</span><span class="sy0">=</span><span class="re2"><span class="nu0">131071</span> &nbsp; &nbsp;</span><br />
# libipulog/ulogd receive buffer size, should be &gt; rmem<br />
<span class="re1">bufsize</span><span class="sy0">=</span><span class="re2">150000</span><br />
# ulogd_BASE.so - interpreter plugin for basic IPv4 header fields<br />
# &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; you will always need this<br />
<span class="re1">plugin</span><span class="sy0">=</span><span class="st0">&quot;/usr/lib/ulogd/ulogd_BASE.so&quot;</span><br />
<span class="re1">plugin</span><span class="sy0">=</span><span class="st0">&quot;/usr/lib/ulogd/ulogd_MYSQL.so&quot;</span><br />
<br />
<span class="re0"><span class="br0">&#91;</span><span class="caps">MYSQL</span><span class="br0">&#93;</span></span><br />
<span class="re1">table</span><span class="sy0">=</span><span class="st0">&quot;ulog&quot;</span><br />
<span class="re1">pass</span><span class="sy0">=</span><span class="st0">&quot;foo&quot;</span><br />
<span class="re1">user</span><span class="sy0">=</span><span class="st0">&quot;ulog&quot;</span><br />
<span class="re1">db</span><span class="sy0">=</span><span class="st0">&quot;ulog&quot;</span><br />
<span class="re1">host</span><span class="sy0">=</span><span class="st0">&quot;localhost&quot;</span><br />
<span class="re1">reconnect</span><span class="sy0">=</span><span class="re2">5</span><br />
<span class="re1">connect_timeout</span><span class="sy0">=</span><span class="re2">10</span></div></div>

<p>The relevant parts of my firewall&nbsp;rules:</p>

<div class="geshifilter"><div class="bash geshifilter-bash" style="font-family:monospace;"><span class="co0"># Count proxy usage (transparent and explicit)</span><br />
iptables <span class="re5">-A</span> count-from-inside <span class="re5">-p</span> <span class="sy0">!</span> tcp <span class="re5">-j</span> <span class="caps">RETURN</span><br />
iptables <span class="re5">-A</span> count-from-inside <span class="re5">-p</span> tcp <span class="re5">-m</span> multiport <span class="re5">&#8212;destination-ports</span> <span class="sy0">!</span> <span class="nu0">3128</span>,<span class="nu0">8080</span> <span class="re5">-j</span> <span class="caps">RETURN</span><br />
iptables <span class="re5">-A</span> count-from-inside <span class="re5">-s</span> 10.0.0.16<span class="sy0">/</span><span class="nu0">28</span> <span class="re5">-j</span> <span class="caps">ULOG</span> <span class="re5">&#8212;ulog-nlgroup</span> <span class="nu0">1</span> <span class="re5">&#8212;ulog-qthreshold</span> <span class="nu0">50</span> <span class="re5">&#8212;ulog-cprange</span> <span class="nu0">4</span> <span class="re5">&#8212;ulog-prefix</span> sr-p<br />
iptables <span class="re5">-A</span> count-from-inside <span class="re5">-s</span> 10.0.0.32<span class="sy0">/</span><span class="nu0">28</span> <span class="re5">-j</span> <span class="caps">ULOG</span> <span class="re5">&#8212;ulog-nlgroup</span> <span class="nu0">1</span> <span class="re5">&#8212;ulog-qthreshold</span> <span class="nu0">50</span> <span class="re5">&#8212;ulog-cprange</span> <span class="nu0">4</span> <span class="re5">&#8212;ulog-prefix</span> fb-p<br />
iptables <span class="re5">-A</span> count-from-inside <span class="re5">-s</span> 10.0.0.128<span class="sy0">/</span><span class="nu0">25</span> <span class="re5">-j</span> <span class="caps">ULOG</span> <span class="re5">&#8212;ulog-nlgroup</span> <span class="nu0">1</span> <span class="re5">&#8212;ulog-qthreshold</span> <span class="nu0">50</span> <span class="re5">&#8212;ulog-cprange</span> <span class="nu0">4</span> <span class="re5">&#8212;ulog-prefix</span> gu-p<br />
<br />
iptables <span class="re5">-A</span> count-to-inside <span class="re5">-p</span> <span class="sy0">!</span> tcp <span class="re5">-j</span> <span class="caps">RETURN</span><br />
iptables <span class="re5">-A</span> count-to-inside <span class="re5">-p</span> tcp <span class="re5">-m</span> multiport <span class="re5">&#8212;source-ports</span> <span class="sy0">!</span> <span class="nu0">3128</span>,<span class="nu0">8080</span> <span class="re5">-j</span> <span class="caps">RETURN</span><br />
iptables <span class="re5">-A</span> count-to-inside <span class="re5">-d</span> 10.0.0.16<span class="sy0">/</span><span class="nu0">28</span> <span class="re5">-j</span> <span class="caps">ULOG</span> <span class="re5">&#8212;ulog-nlgroup</span> <span class="nu0">1</span> <span class="re5">&#8212;ulog-qthreshold</span> <span class="nu0">50</span> <span class="re5">&#8212;ulog-cprange</span> <span class="nu0">4</span> <span class="re5">&#8212;ulog-prefix</span> sr-p<br />
iptables <span class="re5">-A</span> count-to-inside <span class="re5">-d</span> 10.0.0.32<span class="sy0">/</span><span class="nu0">28</span> <span class="re5">-j</span> <span class="caps">ULOG</span> <span class="re5">&#8212;ulog-nlgroup</span> <span class="nu0">1</span> <span class="re5">&#8212;ulog-qthreshold</span> <span class="nu0">50</span> <span class="re5">&#8212;ulog-cprange</span> <span class="nu0">4</span> <span class="re5">&#8212;ulog-prefix</span> fb-p<br />
iptables <span class="re5">-A</span> count-to-inside <span class="re5">-d</span> 10.0.0.128<span class="sy0">/</span><span class="nu0">25</span> <span class="re5">-j</span> <span class="caps">ULOG</span> <span class="re5">&#8212;ulog-nlgroup</span> <span class="nu0">1</span> <span class="re5">&#8212;ulog-qthreshold</span> <span class="nu0">50</span> <span class="re5">&#8212;ulog-cprange</span> <span class="nu0">4</span> <span class="re5">&#8212;ulog-prefix</span> gu-p<br />
<br />
<span class="co0"># Count forwarded traffic (excluding local internet connection - ppp2)</span><br />
iptables <span class="re5">-A</span> count-forward-in <span class="re5">-i</span> ppp2 <span class="re5">-j</span> <span class="caps">RETURN</span><br />
iptables <span class="re5">-A</span> count-forward-in <span class="re5">-d</span> 10.0.0.16<span class="sy0">/</span><span class="nu0">28</span> <span class="re5">-j</span> <span class="caps">ULOG</span> <span class="re5">&#8212;ulog-nlgroup</span> <span class="nu0">1</span> <span class="re5">&#8212;ulog-qthreshold</span> <span class="nu0">50</span> <span class="re5">&#8212;ulog-cprange</span> <span class="nu0">4</span> <span class="re5">&#8212;ulog-prefix</span> sr-f<br />
iptables <span class="re5">-A</span> count-forward-in <span class="re5">-d</span> 10.0.0.32<span class="sy0">/</span><span class="nu0">28</span> <span class="re5">-j</span> <span class="caps">ULOG</span> <span class="re5">&#8212;ulog-nlgroup</span> <span class="nu0">1</span> <span class="re5">&#8212;ulog-qthreshold</span> <span class="nu0">50</span> <span class="re5">&#8212;ulog-cprange</span> <span class="nu0">4</span> <span class="re5">&#8212;ulog-prefix</span> fb-f<br />
iptables <span class="re5">-A</span> count-forward-in <span class="re5">-d</span> 10.0.0.128<span class="sy0">/</span><span class="nu0">25</span> <span class="re5">-j</span> <span class="caps">ULOG</span> <span class="re5">&#8212;ulog-nlgroup</span> <span class="nu0">1</span> <span class="re5">&#8212;ulog-qthreshold</span> <span class="nu0">50</span> <span class="re5">&#8212;ulog-cprange</span> <span class="nu0">4</span> <span class="re5">&#8212;ulog-prefix</span> gu-f<br />
<br />
iptables <span class="re5">-A</span> count-forward-out <span class="re5">-o</span> ppp2 <span class="re5">-j</span> <span class="caps">RETURN</span><br />
iptables <span class="re5">-A</span> count-forward-out <span class="re5">-s</span> 10.0.0.16<span class="sy0">/</span><span class="nu0">28</span> <span class="re5">-j</span> <span class="caps">ULOG</span> <span class="re5">&#8212;ulog-nlgroup</span> <span class="nu0">1</span> <span class="re5">&#8212;ulog-qthreshold</span> <span class="nu0">50</span> <span class="re5">&#8212;ulog-cprange</span> <span class="nu0">4</span> <span class="re5">&#8212;ulog-prefix</span> sr-f<br />
iptables <span class="re5">-A</span> count-forward-out <span class="re5">-s</span> 10.0.0.32<span class="sy0">/</span><span class="nu0">28</span> <span class="re5">-j</span> <span class="caps">ULOG</span> <span class="re5">&#8212;ulog-nlgroup</span> <span class="nu0">1</span> <span class="re5">&#8212;ulog-qthreshold</span> <span class="nu0">50</span> <span class="re5">&#8212;ulog-cprange</span> <span class="nu0">4</span> <span class="re5">&#8212;ulog-prefix</span> fb-f<br />
iptables <span class="re5">-A</span> count-forward-out <span class="re5">-s</span> 10.0.0.128<span class="sy0">/</span><span class="nu0">25</span> <span class="re5">-j</span> <span class="caps">ULOG</span> <span class="re5">&#8212;ulog-nlgroup</span> <span class="nu0">1</span> <span class="re5">&#8212;ulog-qthreshold</span> <span class="nu0">50</span> <span class="re5">&#8212;ulog-cprange</span> <span class="nu0">4</span> <span class="re5">&#8212;ulog-prefix</span> gu-f<br />
<br />
<span class="co0"># Glue</span><br />
iptables <span class="re5">-A</span> <span class="caps">INPUT</span> <span class="re5">-i</span> eth0 <span class="re5">-j</span> count-from-inside<br />
iptables <span class="re5">-A</span> <span class="caps">OUTPUT</span> &nbsp;<span class="re5">-o</span> eth0 <span class="re5">-j</span> count-to-inside<br />
iptables <span class="re5">-A</span> <span class="caps">FORWARD</span> <span class="re5">-i</span> ppp+ <span class="re5">-j</span> count-forward-in<br />
iptables <span class="re5">-A</span> <span class="caps">FORWARD</span> <span class="re5">-o</span> ppp+ <span class="re5">-j</span> count-forward-out</div></div>

<p>So, traffic for my /28 (sr) will be counted as <code>sr-f</code> or <code>sr-p</code> so I can tally up proxy <span class="amp">&amp;</span> forwarded traffic separately. (Yes, I can count traffic with squid too, but doing it all in one place is simpler.) <code>fb</code> is random housemate Foo Bar, and <code>gu</code> guest (unreserved IP&nbsp;addresses).</p>

<p>You can query the usage this month with for&nbsp;example:</p>

<div class="geshifilter"><div class="mysql geshifilter-mysql" style="font-family:monospace;"><a href="http://search.mysql.com/search?site=refman-51&amp;q=SELECT&amp;lr=lang_en"><span class="kw1"><span class="caps">SELECT</span></span></a> oob_prefix<span class="sy2">,</span> <a href="http://dev.mysql.com/doc/refman/5.1/en/group-by-functions-and-modifiers.html"><span class="kw22"><span class="caps">SUM</span></span></a><span class="br0">&#40;</span>ip_totlen<span class="br0">&#41;</span> <a href="http://search.mysql.com/search?site=refman-51&amp;q=FROM&amp;lr=lang_en"><span class="kw1"><span class="caps">FROM</span></span></a> ulog <a href="http://search.mysql.com/search?site=refman-51&amp;q=WHERE&amp;lr=lang_en"><span class="kw1"><span class="caps">WHERE</span></span></a> oob_time_sec <span class="sy1">&gt;</span> <a href="http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html"><span class="kw17">UNIX_TIMESTAMP</span></a><span class="br0">&#40;</span><span class="st0">&#8216;2008-04-01 00:00:00&#8217;</span><span class="br0">&#41;</span> <a href="http://search.mysql.com/search?site=refman-51&amp;q=GROUP%20BY&amp;lr=lang_en"><span class="kw1"><span class="caps">GROUP</span> <span class="caps">BY</span></span></a> oob_prefix<span class="sy2">;</span></div></div>

<p>Your table will fill up fast. We are averaging around 200 000 rows per day. So obviously some caching is in&nbsp;order:</p>

<div class="geshifilter"><div class="mysql geshifilter-mysql" style="font-family:monospace;"><a href="http://search.mysql.com/search?site=refman-51&amp;q=CREATE&amp;lr=lang_en"><span class="kw1"><span class="caps">CREATE</span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=TABLE&amp;lr=lang_en"><span class="kw1"><span class="caps">TABLE</span></span></a> daily <span class="br0">&#40;</span><br />
&nbsp; id <a href="http://search.mysql.com/search?site=refman-51&amp;q=INT&amp;lr=lang_en"><span class="kw4"><span class="caps">INT</span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=UNSIGNED&amp;lr=lang_en"><span class="kw6"><span class="caps">UNSIGNED</span></span></a> <a href="http://dev.mysql.com/doc/refman/5.1/en/non-typed-operators.html"><span class="kw10"><span class="caps">NOT</span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=NULL&amp;lr=lang_en"><span class="kw3"><span class="caps">NULL</span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=AUTO_INCREMENT&amp;lr=lang_en"><span class="kw6">AUTO_INCREMENT</span></a><span class="sy2">,</span><br />
&nbsp; <a href="http://search.mysql.com/search?site=refman-51&amp;q=TIME&amp;lr=lang_en"><span class="kw5">time</span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=TIMESTAMP&amp;lr=lang_en"><span class="kw4"><span class="caps">TIMESTAMP</span></span></a><span class="sy2">,</span><br />
&nbsp; oob_prefix <a href="http://dev.mysql.com/doc/refman/5.1/en/string-functions.html"><span class="kw14"><span class="caps">CHAR</span></span></a><span class="br0">&#40;</span><span class="nu0">4</span><span class="br0">&#41;</span> <a href="http://dev.mysql.com/doc/refman/5.1/en/non-typed-operators.html"><span class="kw10"><span class="caps">NOT</span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=NULL&amp;lr=lang_en"><span class="kw3"><span class="caps">NULL</span></span></a><span class="sy2">,</span><br />
&nbsp; <a href="http://search.mysql.com/search?site=refman-51&amp;q=DATA&amp;lr=lang_en"><span class="kw1">data</span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=INT&amp;lr=lang_en"><span class="kw4"><span class="caps">INT</span></span></a> <a href="http://search.mysql.com/search?site=refman-51&amp;q=UNSIGNED&amp;lr=lang_en"><span class="kw6"><span class="caps">UNSIGNED</span></span></a> <a href="http://dev.mysql.com/doc/refman/5.1/en/non-typed-operators.html"><span class="kw10"><span class="caps">NOT</span></span></a> <a href="http://search.mysql.com/search?site=refman-51&