I am setting up two DNS servers. One is on the firewall/router, the other is an internal server. I have lots of experience setting up DNS servers, so this problem is particularly perplexing.
Machine setup
Firewall external address: 207.62.233.2
Firewall internal address: 10.24.0.1  
Secondary internal address: 10.24.0.21
Master named.conf (relevant portions only)
options {
    listen-on port 53 { any; };
    listen-on-v6 port 53 { any; };
    directory   "/var/named";
    dump-file   "/var/named/data/cache_dump.db";
    statistics-file "/var/named/data/named_stats.txt";
    memstatistics-file "/var/named/data/named_mem_stats.txt";
    recursion yes;
};
view "internal" {
    match-clients { 10.24.0.0/16; 127.0.0.1; };
    match-recursive-only yes;
    allow-recursion { clients; };
    allow-transfer { 10.24.0.21; };
    zone "ct.sierracollege.edu" {
        type master;
        file "data/db.ct.int";
    };
    include "/etc/named.rfc1912.zones";
    zone "." IN {
        type hint;
        file "named.ca";
    };
};
view "external" {
    recursion no;
    match-clients { any; };
    allow-transfer { any; }; // temporarily allowed for debugging purposes
    zone "ct.sierracollege.edu" {
            type master;
            file "data/db.ct.ext";
    };
};
What works
The split DNS on the firewall works great. If I query it from an internal machine, I get internal answers. Likewise, querying it from an external machine gives me external answers. Here's an internal query.
# dig @10.24.0.1 ct1.ct.sierracollege.edu
; <<>> DiG 9.5.1-P2 <<>> @10.24.0.1 ct1.ct.sierracollege.edu
; (1 server found)
;; global options:  printcmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 51024
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 2, ADDITIONAL: 2
;; QUESTION SECTION:
;ct1.ct.sierracollege.edu.  IN  A
;; ANSWER SECTION:
ct1.ct.sierracollege.edu. 3600  IN  A   10.24.0.11
;; AUTHORITY SECTION:
ct.sierracollege.edu.   3600    IN  NS  cs.sierracollege.edu.
ct.sierracollege.edu.   3600    IN  NS  fw.ct.sierracollege.edu.
;; ADDITIONAL SECTION:
cs.sierracollege.edu.   3600    IN  A   10.24.0.21
fw.ct.sierracollege.edu. 3600   IN  A   10.24.0.1
;; Query time: 1 msec
;; SERVER: 10.24.0.1#53(10.24.0.1)
;; WHEN: Wed Jan  6 12:57:02 2010
;; MSG SIZE  rcvd: 124
What doesn't work
Zone transfers don't work correctly. Instead of transferring the internal zone, it transfers the external one. Here's an example, done from the very same internal machine as above:
# dig @10.24.0.1 ct.sierracollege.edu axfr
; <<>> DiG 9.5.1-P2 <<>> @10.24.0.1 ct.sierracollege.edu axfr
; (1 server found)
;; global options:  printcmd
ct.sierracollege.edu.   3600    IN  SOA ct.sierracollege.edu. root.ct.sierracollege.edu. 3 3600 1800 604800 3600
ct.sierracollege.edu.   3600    IN  NS  fw.ct.sierracollege.edu.
ct1.ct.sierracollege.edu. 3600  IN  A   207.62.233.11
ct2.ct.sierracollege.edu. 3600  IN  A   207.62.233.12
ct3.ct.sierracollege.edu. 3600  IN  A   207.62.233.13
fw.ct.sierracollege.edu. 3600   IN  A   207.62.233.2
ct.sierracollege.edu.   3600    IN  SOA ct.sierracollege.edu. root.ct.sierracollege.edu. 3 3600 1800 604800 3600
;; Query time: 2 msec
;; SERVER: 10.24.0.1#53(10.24.0.1)
;; WHEN: Wed Jan  6 13:01:37 2010
;; XFR size: 7 records (messages 1, bytes 208)
Entries from the /var/log/messages file show that the external view is being hit:
Jan  6 13:01:37 fw named[17572]: client 10.24.0.21#42362: view external: transfer of 'ct.sierracollege.edu/IN': AXFR started
Jan  6 13:01:37 fw named[17572]: client 10.24.0.21#42362: view external: transfer of 'ct.sierracollege.edu/IN': AXFR ended
Consequently, my slaves directory is being filled with external zone files, not the internal ones.
Any ideas?