Author Archives: Michael Kuron

ISC DHCPd: Dynamic DNS updates against secure Microsoft DNS

UPDATE 2016: I have posted a much simpler way that works with DNS delegations so that you can have your domain controllers maintain the records necessary for their discovery in Microsoft DNS, while all your clients are in a BIND DNS server which can be easily interfaced with ISC DHCPd.

ISC DHCPd is capable of Dynamic DNS updates against servers like BIND that support shared-key authentication or any other server that supports unauthenticated updates (such as BIND or Microsoft DNS with secure updates disabled).

So, what to do if you want to run ISC DHCPd on your Windows network, which is obviously running Microsoft’s DNS server? BIND’s nsupdate tool supports Microsoft’s Kerberos authentication scheme when using the -g flag (the -o flag is only necessary for Windows 2000 Server, but not anymore for Windows Server 2008 R2), and DHCPd supports on commit/release/expiry blocks that let you run scripts upon these events. So here is my script:

#!/bin/bash

## CONFIGURATION ##

realm=EXAMPLE.COM
principal=dhcpduser@$realm
keytab=/root/dhcpduser.keytab
domain=example.com
ns=example-domain01.example.com

export KRB5CCNAME="/tmp/dhcp-dyndns.cc"

keytab can be generated using

$ ktutil

ktutil: addent -password -p dhcpduser@EXAMPLE.COM -k 1 -e aes256-cts-hmac-sha1-96

Password for dhcpduser@EXAMPLE.COM:

ktutil: wkt dhcpduser.keytab

ktutil: quit

VARIABLES

action=$1
ip=$2
name=$(echo $3 | awk -F '.' '{print $1}')
mac=$4

usage()
{
echo "USAGE:"
echo $0 add 192.0.2.123 testhost 00:11:22:33:44:55
echo $0 add 192.168.0.127 "" 00:11:22:44:33:55
echo $0 delete 192.0.2.123 testhost 00:11:22:33:44:55
echo $0 delete 192.0.2.127 "" 00:11:22:44:33:55
}

if [ "$ip" = "" ]; then
echo "IP missing"
usage
exit 101
fi
if [ "$name" = "" ]; then
#echo "name missing"
#usage
#exit 102
name=$(echo $ip | awk -F '.' '{print "dhcp-"$1"-"$2"-"$3"-"$4}')

if [ "$action" = "delete" ]; then
name=$(host $ip | awk '{print $5}' | awk -F '.' '{print $1}')

echo $name | grep NXDOMAIN 2>$1 >/dev/null
if [ "$?" = "0" ]; then
exit 0;
fi
fi
fi

ptr=$(echo $ip | awk -F '.' '{print $4"."$3"."$2"."$1".in-addr.arpa"}')

KERBEROS

#export LD_LIBRARY_PATH=/usr/local/krb5-1.7/lib
#export PATH=/usr/local/krb5-1.7/bin:$PATH

klist 2>&1 | grep $realm | grep '/' > /dev/null
if [ "$?" = 1 ]; then
expiration=0
else
expiration=$(klist | grep $realm | grep '/' | awk -F ' ' '{system ("date -d \""$2"\" +%s")}' | sort | head -n 1)
fi

now=$(date +%s)
if [ "$now" -ge "$expiration" ]; then
echo "Getting new ticket, old one expired $expiration, now is $now"
kinit -F -k -t $keytab $principal
fi

NSUPDATE

case "$action" in
add)
echo "Setting $name.$domain to $ip on $ns"

oldname=$(host $ip $ns | grep "domain name pointer" | awk '{print $5}' | awk -F '.' '{print $1}')
if [ "$oldname" = "" ]; then
oldname=$name
elif [ "$oldname" = "$name" ]; then
oldname=$name
else
echo "Also deleting $oldname A record"
fi

nsupdate -g <
server $ns
realm $realm
update delete $oldname.$domain 3600 A
update delete $name.$domain 3600 A
update add $name.$domain 3600 A $ip
send
UPDATE
result1=$?
nsupdate -g <
server $ns
realm $realm
update delete $ptr 3600 PTR
update add $ptr 3600 PTR $name.$domain
send
UPDATE
result2=$?
;;

delete)
echo "Deleting $name.$domain to $ip on $ns"
nsupdate -g <
server $ns
realm $realm
update delete $name.$domain 3600 A
send
UPDATE
result1=$?
nsupdate -g <
server $ns
realm $realm
update delete $ptr 3600 PTR
send
UPDATE
result2=$?
;;
*)
echo "Invalid action specified"
exit 103
;;
esac

result=$result1$result2
if [ "$result" != "00" ]; then
echo "DHCP-DNS Update failed: $result"
logger "DHCP-DNS Update failed: $result"
fi

exit $result

and here is the relevant part of my dhcpd.conf:

on commit {
set noname = concat("dhcp-", binary-to-ascii(10, 8, "-", leased-address));
set ClientIP = binary-to-ascii(10, 8, ".", leased-address);
set ClientMac = binary-to-ascii(16, 8, ":", substring(hardware, 1, 6));
set ClientName = pick-first-value(option host-name, host-decl-name, config-option host-name, noname);
log(concat("Commit: IP: ", ClientIP, " Mac: ", ClientMac, " Name: ", ClientName));

execute("/root/dhcp-dyndns.sh", "add", ClientIP, ClientName, ClientMac);
}
on release {
set ClientIP = binary-to-ascii(10, 8, ".", leased-address);
set ClientMac = binary-to-ascii(16, 8, ":", substring(hardware, 1, 6));
log(concat("Release: IP: ", ClientIP, " Mac: ", ClientMac));

cannot get a ClientName here, for some reason that always fails

execute("/root/dhcp-dyndns.sh", "delete", ClientIP, "", ClientMac);
}
on expiry {
set ClientIP = binary-to-ascii(10, 8, ".", leased-address);

cannot get a ClientMac here, apparently this only works when actually receiving a packet

log(concat("Expired: IP: ", ClientIP));

cannot get a ClientName here, for some reason that always fails

execute("/root/dhcp-dyndns.sh", "delete", ClientIP, "", "0");
}

Figuring this all out took me several afternoons because Kerberos 5 1.8 has a bug where forwardable tickets (which is the default on Debian) are incompatible with nsupdate. Manually compiling 1.7 or getting 1.9 from the experimental Debian branch helps, as does adding the -F flag to kinit (which I did in the script above) to make the ticket non-forwardable.
I filed a bug with Debian (#611906) and Sam Hartman (thanks!) helped me track it down.

EDIT 2011-11-17:
I recently ran into the issue that if the AD server could not be reached, dhcpd would stall (and not respond to DHCP requests during that time) until nsupdate reached its timeout. The fix is simple: rename dhcp-dyndns.sh to dhcp-dyndns-real.sh and create dhcp-dyndns.sh with the following contents to fork off the real script into the background:
#!/bin/bash

$(dirname $0)/dhcp-dyndns.sh $@ 2>&1 | logger &

Also, I updated the on commit section in the dhcpd.conf excerpt above to compose a fallback name from the IP address if the client provides no hostname. This fixes the issue that nsupdate tries to register a record based on the name and fails.

Extending Active Directory for Mac OS X clients

After I wrote about building your own OpenDirectory server on Linux a while back, I decided to do the same thing on Windows Server 2008 R2. The process of extending the AD schema to include Apple classes and attributes is documented by Apple (this is the Leopard version of the document – if you don’t plan on having exclusively Snow Leopard clients, you can follow the newer version of the document that skips a couple of things that Snow Leopard no longer needs).

But since schema extensions are generally frowned upon in the Windows world because they’re irreversible (why the heck, Microsoft…?), I initially tried a dual-directory (golden triangle, magic triangle) type approach where I’d be augmenting my AD with Apple records coming from an AD LDS (Active Directory Lightweight Directory Services, previously called ADAM, Active Directory User Mode, which is basically a plain LDAP server from Microsoft). While this may sound like a great idea, I just couldn’t get it to work. After dozens of manual schema extensions to AD LDS (Microsoft doesn’t include many standard LDAP attributes, so I had to dig through the dependencies of apple.schema and even tried importing a complete OD schema), I gave up because I could not get Workgroup Manager to authenticate against it to allow me to make changes.

So the next thing to do was follow Apple’s AD schema extension guide (linked above) and do what everybody else did. This was rather straight-forward (managed preferences for users, groups and computers worked right away), but when I tried to create a computer list (which is not possible using Snow Leopard’s Server Admin Tools, but requires Tiger’s (which throw loads of errors on Snow Leopard but still get the job done) since Leopard introduced computer groups which however are not supported by the AD plugin), it just said I didn’t have permission to do that. After enabling DirectoryService debug logging (killall -USR1 DirectoryService && killall -USR2 DirectoryService), I traced it down to Active Directory: Add record CN=Untitled_1,CN=Mac OS X,DC=xxx,DC=zz with FAILED – LDAP Error 19 in /Library/Logs/DirectoryService/*. Apparently, that’s caused by some versions of ADSchemaAnalyzer setting objectClassCategory to 0 instead of 1 on all exported classes. Too bad AD schema extensions are irreversible and that’s one of the attributes you can’t change later on… 🙁 Well, with AD Schema Management MMC snap-in, I was able to rename the botched apple-computer-list class, defunct it and add a new one using ldifde. With some really wild hacking in the AD Schema using ADSI Editor, I was then able to  eventually get OS X to no longer look at the renamed attribute, but instead at the new one. To see whether you have been successful, killall DirectoryService, wait a few seconds and grep -H computer-list /Library/Preferences/DirectoryService/ActiveDirectory* will show a line indicating which class in the schema it’s using.

Once you’re there, everything should work as expected. If you don’t want to use Tiger’s Workgroup Manager to create old-style computer lists, you can do that in ADSI Editor and create apple-computer-list objects in the CN=Mac OS X branch by hand.

So, attached is the schema ldif that’s exactly the way it should be. I really wonder why Apple doesn’t provide it themselves – it’s going to turn out exactly like that every time you follow their guide on any Windows server… Apple Schema for Active Directory

I guess that the overall conclusion of this should be that AD schema extensions in general and specifically Mac OS X managed clients in AD environments are a nasty hack. I suppose the dual directory/magic triangle/golden triangle approach with a Microsoft AD and an Apple OD would work, but it requires maintaining two separate directories, which may not be that great in a larger environment either.

If Apple discontinues Mac OS X Server at some point in the near future (which the demise of the Xserve and the lack of announcements regarding Mac OS X 10.7 Server alongside Mac OS X Lion suggest), this is definitely something they need to improve. There are some third-party solutions that store MCX settings outside of AD (similar to Windows GPOs, which are stored on the SYSVOL share) such Thursby ADmitMac – however that’s a rather expensive solution (a dozen client licenses costs about as much as two Mac mini servers) and might break after OS updates (though from what I’ve heard, they’re rather quick at providing updates). If Apple does discontinue Mac OS X Server, they should definitely improve Lion’s AD integration to replicate ADmitMac’s features.

HTML to ePub using Sigil

I was looking for a way to convert HTML books into an ePub file. The general layout of the file should be preserved (including images), while all the stuff that doesn’t make sense on an ebook reader (such as navigation elements and the usual “back to top” links) should be removed.

After trying Calibre rather extensively, I came across an app named Sigil, which does exactly what I want: You just throw in your HTML files (it automatically imports images referenced by them) and add some metadata.

Before proceeding, you should use your favorite scripting language (or modify the attached quick-and-dirty PHP script) to remove everything but the main part of the chapter from the HTML files. (Make sure to remove any tables or divs surrounding the entire content because that might break page-by-page navigation on your ebook reader).

Sigil works very smooth if your HTML files are in alphabetical order. If they’re not, don’t despair: take the index.html file that (hopefully) came with them and us your favorite scripting language (or modify the attached quick-and-dirty PHP script) to grab all the links from it (be sure to remove anchors and duplicates) and generate an XML structure like <spine toc="ncx">
<itemref idref="file1.html" />
<itemref idref="file2.html" />
</spine>
. Manually replace the spine section in the content.opf file inside the generated ePub with the lines you just created. Then re-open the ePub in Sigil and check whether it found any HTML files you forgot to include (they will show up at the top of the file list) – if there are any, move them to the place where you want them.

Once you have everything the way you want it, check the auto-generated table of contents using the TOC Editor option. Chances are that you have everything in there duplicated if the links in your index.html file are recognized as chapter headlines. In that case, just uncheck those (if you don’t feel like unchecking 500 items, I’ve attached an AppleScript to do that, just select the bottom-most line you want unchecked and adjust the number of lines inside the script).

iOS 4.1: Undocumented VPN API, used by Cisco AnyConnect

A few days ago, Cisco AnyConnect was admitted to the App Store. This was mentioned by a few blogs, but they didn’t seem to notice the relevance of it. AnyConnect is an enterprise SSL VPN technology by Cisco, so this may not seem relevant to all that many people at first sight.
However, in order to implement a VPN client to provide VPN connectivity for other apps, you need to hook into the operating system’s network stack. On the iOS App Store, everybody knows that Apple is rather strict on what a developer can do — hooking into the OS kernel and providing network functionality to other apps isn’t something they provide APIs for (and therefore don’t allow).

Wondering how Cisco got around these App Store limitations, I took a closer look at the AnyConnect app. Upon first starting it, it asks whether you want to let it “extend… the Virtual Private Network (VPN) capabilities of your device”.

Cisco AnyConnect Secure Mobility Client extends the Virtual Private Network (VPN) capabilities of your device. Do you want to enable this software? Don’t Allow / OK

After entering a VPN server name etc., I switched over to the Settings app and noticed that the newly created AnyConnect VPN showed up as a system-wide VPN (though if you try to edit it from there, it’ll just say that you should use the AnyConnect app instead).

To configure the settings for ….., use the app provided by Cisco.

Odd, how would an app be able to do any of this if it’s not allowed to get involved with iOS deeper than the App Store guidelines would allow?

Moving on and digging deeper into the .ipa bundle:

The Payload contains AnyConnectDataAgent.vpnplugin, in addition to the AnyConnect.app:

AnyConnectDataAgent.vpnplugin

AnyConnect.app‘s Entitlements.plist contains an entitlement named com.apple.networking.vpn.configuration:

com.apple.networking.vpn.configuration

Neither vpnplugin bundles nor the com.apple.networking.vpn.configuration entitlement are documented anywhere (at least not in a way that can be found through Google). Since this appears to be a special iOS API created by Apple specifically for Cisco, the question is whether it’s also open to other developers. The only other app I’ve found that uses it is Juniper Junos Pulse, which was posted a few weeks before the AnyConnect app. Since both Junos Pulse and AnyConnect have in common that they require iOS 4.1, I think it’s safe to assume that 4.1 introduced the API they use.
Personally, I’d be interested to see an OpenVPN client for the iPhone. I’m not sure whether that will ever happen though if this VPN API only exits semi-officially, especially since OpenVPN is not backed by a big company like Cisco or Juniper.

Update 2011: The German computer magazine c’t wrote about the API, but couldn’t make much sense of it either.

Update 2012: The current list of apps using this API consists of: Juniper Junos Pulse (released September 2010, requires iOS 4.1), Cisco AnyConnect (released September 2010, requires iOS 4.1), F5 BIG-IP (released December 2010, requires iOS 4.2), SonicWall Mobile Connect (released December 2011, requires iOS 4.2), Aruba VIA (released December 2011, requires iOS 4.3) and CheckPoint Mobile VPN (released March 2012, requires iOS 5.0).

Update 2012: Googling for the VPN entitlement now not only finds my blog post, but also configd source code at Apple: first in OS X 10.7 sources and later in OS X 10.8 sources (but not in OS X 10.6, which iOS 4 was based on). In the older version, inside an ifdef checking for iPhone OS, a constant named kSCVPNFilterEntitlementName is declared containing that entitlement, but the constant never gets used. In OS X 10.8, it is no longer ifdeffed to the iPhone OS and actually gets used for allowing limited access to the global preferences.plist.

Update 2012: Playing around with AnyConnect again, I noticed that it now uses a generic utun network interface, which is similar to a Linux-style layer 3 tun interface. utun is used for example by Mac OS X’s Back to my Mac feature: the kernel implementation in Mac OS X 10.8.2 can be found in the XNU source at bsd/net/if_utun.c. Some details can be found in Levin, Jonathan. Mac OS X and iOS Internals: To the Apple’s Core. Chapter 17, Layer II, Case Study: utun. Wiley, 2012.

I have received several more reports of unsuccessful attempts to get access to the VPN API. Apparently despite now offering a generic utun interface, Apple continues to be very strict about it. And to date, there still isn’t an OpenVPN client for iOS.

Update 2013: OpenVPN Connect was released today. It supports tun-style OpenVPN connections. Hooray, finally we can use OpenVPN on iOS! Apparently it can even be managed using the “Custom SSL” option in iPhone Configuration Utility.

3rd party Exchange ActiveSync servers

I’ve recently been looking for a comprehensive list of mail/groupware servers/services that offer Exchange ActiveSync integration, e.g. for the iPhone or Windows Mobile. Since I couldn’t find one, I’m putting together a list myself. (I’m not including Microsoft Exchange Server and all those Hosted Exchange solutions, since they’re pretty obvioius choices.)

Article last updated on 2013-08-01.

Note that Google Mail and the (discontinued) free tiers of Google Apps for Business dropped EAS support for new devices in January 2013.

Services:

  • Google Apps for Business
  • Outlook.com (free)
  • MyKolab, which is protected by Switzerland’s strong privacy laws
  • Rackspace
  • Zoho
  • Office 365
  • Atmail
  • NuevaSync Premium: works with any IMAP mail server
  • MailEnable

Groupware software: (many of these are also available as hosted solutions from various hosters)

  • Kolab
  • Zarafa (uses Z-Push)
  • Horde
  • Tine 2.0

IMAP bridges:

The best-looking free solutions are Kolab (if you need a complete groupware) and PHP Push 2 (if you already have IMAP, CalDAV and CardDAV servers or want to run Owncloud for the latter two, have an existing IMAP server and run Roundcube as webmail).

If you know any others, please go ahead and add them to the comments, and I’ll add them to this list.

Slim down Final Cut Studio’s Media Content using HFS Compression

A full installation of Final Cut Studio 3 with all media content (for Motion, DVD Studio Pro, and Soundtrack Pro Loops) takes up around 40-50 GB of hard drive space.
How about regaining 5-10 GB of precious by enabling HFS compression for these folders? Since HFS compression is completely transparent, there are no adverse effects to expect (other than browsing the content libraries being almost unnoticeably slower).

To start, you’ll need a command-line tool called afsctool which can compress (and, amongst other features, decompress) folders using HFS compression. The command you’ll need to run is e.g. sudo afsctool -c -l -k -v -i -9 /Library/Application\ Support/Final\ Cut\ Studio. This compresses all files the given folder using the highest possible compression, verifies its results, prints out the names of files it is unable to compress, and outputs statistics once it’s done.

Some of the folders I compressed:
/Library/Application Support/Final Cut Studio/ (contains Motion and DVD Studio Pro templates): 22.5% compression savings
/Library/Application Support/LiveType/ (contains Motion’s LiveType fonts): 11.4% compression savings
/Library/Application Support/GarageBand/ (contains GarageBand’s  instruments and learning-to-play stuff): 14.3% compression savings
/Library/Application Support/iDVD/ (contains iDVD’s themes): 19.5% compression savings
/Library/Audio/Apple Loops/ (contains GarageBand’s and Soundtrack Pro’s loops): 4.1%
/Library/Audio/Impulse Responses/ (contains  Soundtrack Pro’s impulse response data): 41.3% compression savings

Looking at the compression savings: everything that contains high-quality video can be compressed by around 20%, while audio which is already heavily compressed only yields around 5%. The most amazing result though are the 40% by which the Impulse Responsed were compressed – apparently, these are uncompressed AIFF audio files and thus ideal for compression.

Obviously, your mileage may vary and I’m not responsible if you compress too much and break your system (I’m sure there is a reason why Apple didn’t compress all system files). However, compressing the iLife and Final Cut Studio media content appears safe, I haven’t noticed any unwanted side-effects and it seems well worth trying if you’d like to regain a few gigabytes.

USB to SATA/IDE adapter

If you often find yourself needing to temporarily connect bare hard drives (2.5″, 3.5″ or 5.25″) to your computer, a USB to SATA/IDE adapter is a nice investment.

A while ago, I bought the Sharkoon DriveLink. The nice thing about it is that it can run 2.5″ hard drives without the power brick. However, it would randomly disappear from the computer, interrupting any file transfers that may have been in progress. I returned it and got a replacement unit that showed the exact same behavior, so I returned it for a refund.

Instead, I bought the Scythe Kama Connect 2. So far, it’s been working like a charm.

Building your own OpenDirectory server on Linux

OpenDirectory is a feature included with Mac OS X Server. Wouldn’t it be nice if you could use it without having to spend hundreds of dollars on a server license? Wouldn’t it be great if you could add it into your existing Linux-based OpenLDAP server? It’s actually quite easy because OpenDirectory is a standard OpenLDAP server with a special Apple schema.

0. Prerequisites
– OpenLDAP server with Samba integration (I’m runnig it on a Ubuntu 8.04 server, using the standard OpenLDAP and Samba packages). I won’t go into the details of how to set this up, there are lots of tutorials around the web on this.
– some kind of LDAP admin tool, I used phpLDAPAdmin
– Mac OS X 10.5 Leopard clients

1. Adding the Apple schema to your OpenDirectory server
It is located in /etc/openldap/schema/apple.schema on any Mac. Copy this file to your OpenLDAP server and add it to your slapd.conf.
You may run into the problem that apple.schema references some samba.schema entries that were deprecated with Samba 3. Specifically, these are acctFlags, pwdLastSet, logonTime, logoffTime, kickoffTime, homeDrive, scriptPath, profilePath, userWorkstations, smbHome, rid and primaryGroupID, so you’ll need to editapple.schema and replace these with their Samba 3 counterparts.
Now, restart the OpenLDAP daemon so it recognizes the changes.

2. Adding some Mac-specific attributes to your LDAP server
Add an ou=macosx branch to your LDAP tree, under which you’ll need to create ou=accesscontrols, ou=augments, ou=automountMap, ou=autoserversetup, ou=certificateauthorities, ou=computer_groups, ou=computer_lists, ou=computers, ou=filemakerservers, ou=locations, ou=machines, ou=maps, ou=mount, ou=neighborhoods, ou=places, ou=preset_computer_groups, ou=preset_computer_lists, ou=preset_computers, ou=preset_groups, ou=preset_users, ou=printers, and ou=resources.
To all your LDAP groups, add the apple-group objectClass. To all your LDAP users, add the apple-user objectClass.

3. Connecting your Mac to the LDAP directory
On your Mac, go into Directory Access and add your LDAP server. Choose OpenDirectory as the server type and adjust the Samba mappings to match your changes from step 1. Here is a plist you can import into Directory Access that already has these mappings corrected: LDAPv3_Unix_Samba3_OD.plist.
If you want your other clients to automatically use this mapping, create a cn=config branch in your LDAP tree and use the Write to Server button in Directory Access.

4. Use Workgroup Manager to set network home folders, managed preferences, …
Now, you can use Workgroup Manager to manage network home folders and managed preferences, just like you would on a Mac server.  You’ll need to authenticate using an LDAP user who has full write privileges to the directory (as set in slapd.conf). The standard cn=admin,dc=example,dc=com user will NOT work.

5. Conclusion
Almost everything works, except for:
– adding new users and group through Workgroup Manager
– solution: unknown
– assigning directory admin privileges to users through Workgroup Manager
– solution: using an OpenLDAP server set up to use cn=config instead of slapd.conf. This will also require going into Directory Access again and adding the OLCBDBConfig, OLCFrontEndConfig, OCGlobalConfig, OLCSchemaConfig and OLCOverlayDynamicID record types back in (they are included in the OpenDirectory mapping, but I deleted them from mine because they only cause error messages on an OpenLDAP server with slapd.conf configuration).

Here are all the web sites that helped me in the process of figuring this out:
http://docs.info.apple.com/article.html?path=ServerAdmin/10.4/en/c6od15.html (this one is especially important because it explains what to do if your LDAP server is not set up for SASL authentication)
http://www.emmes-world.de/mac-afp-homes.html (this one describes a similar setup and was my most important resource)
http://rajeev.name/blog/2006/09/09/integrating-mac-os-x-into-unix-ldap-environment-with-nfs-home-directories/
http://www.netmojo.ca/blog/tag/ldap/
http://www.macdevcenter.com/pub/a/mac/2003/08/26/active_directory.html?page=2

7. Further Information
Since you’re not using Kerberos for authentication, you may want to look at securing your LDAP connections with SSL. Here are some links that talk about it:
http://www.novell.com/coolsolutions/feature/19965.html
http://www.afp548.com/article.php?story=20071203011158936

Someone else also wrote a blog post about Setting up a Linux server for OS X clients, in which they also describe how to incorporate Kerberos into the whole equation. That’s certainly something worth considering.